远程红外模块驱动

远程红外收发器驱动

介绍

参考《HAL快速入门与项目实战(基于DshanMCU-F407)_V1.0》

参考《ir_remote_datasheet》

参考 红外遥控的工作原理

这里使用 NEC 协议

NEC 红外传输协议

NEC IR 传输协议使用逻辑位的脉冲宽度调制进行编码。每个脉冲突发(标记 - RC 发射器打开)的长度为 562.5μs,载波频率为 38kHz (26.3μs)。逻辑位传输如下:

  • 逻辑 "0"——562.5μs 脉冲突发,后跟 562.5μs 间隔,总传输时间为 1.125ms
  • 逻辑 "1"——562.5μs 脉冲突发,后跟 1.6875ms 间隔,总传输时间为 2.25ms

提示:

使用 NEC IR 传输协议发送或接收远程控制代码时,当载波频率(用于调制/解调)设置为 38.222kHz 时,WB_IRRC 性能最佳

当远程红外发射器开始发送数据时,传输的信息按顺序包含以

下内容:

  • 9ms 前导脉冲突发(逻辑位所用脉冲突发长度的 16 倍(562.5us * 16))
  • 4.5ms 间隔
  • 8 位接收设备的地址
  • 地址的 8 位逻辑反转
  • 8 位命令
  • 命令的 8 位逻辑反转
  • 最后的 562.5μs 脉冲突发表示信息传输结束。

四个字节的数据位均首先发送最低有效位(LSB)。

图 1 说明了 NEC IR 传输帧的格式,地址码为 (00h 00000000b),命令码为 (ADh 10101101b)。

图1

从图 1 中注意到,传输 16 位地址(地址 + 反转)和 16 位命令(命令 + 反转)需要 27ms。

这个结果来自于始终包含的八个 "0" 和八个 "1" 的每个 16 位块,即 (8 * 1.125ms) + (8 * 2.25ms)。

完全传输消息帧需要 67.5 毫秒(不包括表示消息结束的最后 562.5 微秒脉冲突发)

重复码

如果按住遥控器上的按键,将发出重复码,通常在表示消息结束的 562.5 微秒脉冲突发后 40 毫秒左右。

重复码将继续以 108ms 的间隔发送出去,直到最终松开按键。重复码按顺序由以下部分组成:

  • 9ms 前导脉冲突发
  • 2.25ms 间隔
  • 562.5μs 脉冲突发,用于标记间隔的结束(以及所传输的重复代码的结束)。

图 2 说明了发送初始消息帧后两个重复代码的传输。

图2

NEC 协议解读

如上图中所示绿色部分并不是所谓的高低电平中的高电平,而是 38 kHz 的高频脉冲信号(一个脉冲周期是26.3μs)

高频脉冲

其中高电平的占空比是1/4

起始位格式

起始位

在最初的 9ms 脉冲突发中,红外发射二极管发射的都是 38 kHz 的
高频脉冲信号(约342个脉冲周期,可以理解为红外发射二极管不停亮灭342次)之后紧跟着 4.5 ms 的空闲信号,这是 13.5 ms 的起始信号,告诉接受设备要开始接收数据了。

地址码

地址码由 8 位 0 或者 1 组成,逻辑 "0" 或者 逻辑 "1" 并不单单是用高低电平表示,黄色部分依然是高频脉冲。

逻辑位

逻辑 "0" 是由 562.5 μs 的高频脉冲(约21个脉冲周期)和 562.5 μs 的低电平表示,

逻辑 "1" 是由 562.5 μs 的高频脉冲和 1687.5 μs 的低电平表示。

数据码

同理,数据码,也可称为命令码,的长度也为 8 位,逻辑位相同。地址反码和数据反码的作用是用来校验地址码和数据码的正确性。

一个地址码和一个数据码的实际长度都是 16 位,其中包含 8 位逻辑 "0" 和 "1"

传输 16 位地址码(地址码 + 反转反码)和 16 位数据码(数据码 + 数据反转)都需要 27ms。

这个结果来自于始终包含的八个逻辑 "0" 和八个逻辑 "1" 的每个 16 位块,即 (8 * 1.125ms) + (8 * 2.25ms)。

在一帧信号的最后是 562.5 μs 的高频脉冲。

因此在不包含一帧信号的最后 562.5 μs 的高频脉冲的情况下,一帧信号的周期为 67.5 ms

持续按键

如果一直按下按键,并不会连续发送数据码,而是会发送重复码。

重复码

一个重复码包含 9ms 的高频脉冲,2.25ms 的低电平,562.5μs 的高频脉冲,一个重复码的周期约是 110ms

经过处理的信号

三脚红外接收器内置芯片会将 38kHz 的高平脉冲信号转化成低电平,这样对于较多的高频脉冲就变成了低电平,最后只需要 34 个脉冲即可表示一帧信息,极大方便了编程工作。

驱动代码

driver_ir_remote.h

#ifndef __DRIVER_IR_REMOTE_H__
#define __DRIVER_IR_REMOTE_H__

#include "main.h"

#ifdef __cplusplus
extern "C"{
#endif

/**
 * @brief ir_remote max range definition
 */
#ifndef IR_REMOTE_MAX_RANGE
    #define IR_REMOTE_MAX_RANGE             0.20f        /**< 20% */
#endif

#ifndef IR_REMOTE_DECODE_BUF_MAX_LEN
    #define IR_REMOTE_DECODE_BUF_MAX_LEN     127
#endif


/**
 * @brief ir_remote status enumeration definition
 * 
 * 用来定义一帧信号的状态
 */
typedef enum
{
    IR_REMOTE_STATUS_OK            = 0x00,        /**< ok */
    IR_REMOTE_STATUS_REPEAT        = 0x01,        /**< repeat */
    IR_REMOTE_STATUS_ADDR_ERR      = 0x02,        /**< addr error */
    IR_REMOTE_STATUS_CMD_ERR       = 0x03,        /**< cmd error */
    IR_REMOTE_STATUS_FRAME_INVALID = 0x04,        /**< frame invalid */
} ir_remote_status_t;

/**
 * @brief ir_remote structure definition
 * 
 * 用来定义一帧信号的结构体
 */
typedef struct ir_remote_s
{
    uint8_t status;        /**< status */
    uint8_t address;       /**< address */
    uint8_t command;       /**< command */
} ir_remote_t;

/**
 * @brief ir_remote time structure definition
 * 
 * 用来定义一次脉冲开始时的时间戳
 */
typedef struct ir_remote_time_s
{
    uint64_t s;         /**< second */
    uint32_t us;        /**< microsecond */
} ir_remote_time_t;

/**
 * @brief ir_remote decode structure definition
 * 
 * 用来定义脉冲序列中一次脉冲开始时的时间戳和一次脉冲时长
 */
typedef struct ir_remote_decode_s
{
    ir_remote_time_t t;        /**< timestamp */
    uint32_t diff_us;          /**< diff us */
} ir_remote_decode_t;

/**
 * @brief ir_remote handle structure definition
 */
typedef struct ir_remote_handle_s
{
    uint8_t (*timestamp_read)(ir_remote_time_t *t);             /**< point to an timestamp_read function address */
    void (*delay_ms)(uint32_t ms);                              /**< point to a delay_ms function address */
    void (*debug_print)(const char *const fmt, ...);            /**< point to a debug_print function address */
    void (*receive_callback)(ir_remote_t *data);                /**< point to a receive_callback function address */
    uint8_t inited;                                             /**< inited flag */
    
    /* decode 用来缓存记录脉冲序列 */
    ir_remote_decode_t decode[IR_REMOTE_DECODE_BUF_MAX_LEN];    /**< decode buffer */
    /* decode 用来记录脉冲序列长度 */
    uint16_t decode_len;                                        /**< decode length */
    
    ir_remote_time_t last_time;                                 /**< last time */
    ir_remote_t last_code;                                      /**< last code */
} ir_remote_handle_t;

/**
 * @brief ir_remote information structure definition
 */
typedef struct ir_remote_info_s
{
    char chip_name[32];                /**< chip name */
    char manufacturer_name[32];        /**< manufacturer name */
    char interface[8];                 /**< chip interface name */
    float supply_voltage_min_v;        /**< chip min supply voltage */
    float supply_voltage_max_v;        /**< chip max supply voltage */
    float max_current_ma;              /**< chip max current */
    float temperature_min;             /**< chip min operating temperature */
    float temperature_max;             /**< chip max operating temperature */
    uint32_t driver_version;           /**< driver version */
} ir_remote_info_t;

/**
 * @brief     initialize ir_remote_handle_t structure
 * @param[in] HANDLE points to an ir_remote handle structure
 * @param[in] STRUCTURE is ir_remote_handle_t
 * @note      none
 */
#define DRIVER_IR_REMOTE_LINK_INIT(HANDLE, STRUCTURE)         memset(HANDLE, 0, sizeof(STRUCTURE))

/**
 * @brief     link timestamp_read function
 * @param[in] HANDLE points to an ir_remote handle structure
 * @param[in] FUC points to a timestamp_read function address
 * @note      none
 */
#define DRIVER_IR_REMOTE_LINK_TIMESTAMP_READ(HANDLE, FUC)    (HANDLE)->timestamp_read = FUC

/**
 * @brief     link delay_ms function
 * @param[in] HANDLE points to an ir_remote handle structure
 * @param[in] FUC points to a delay_ms function address
 * @note      none
 */
#define DRIVER_IR_REMOTE_LINK_DELAY_MS(HANDLE, FUC)          (HANDLE)->delay_ms = FUC

/**
 * @brief     link debug_print function
 * @param[in] HANDLE points to an ir_remote handle structure
 * @param[in] FUC points to a debug_print function address
 * @note      none
 */
#define DRIVER_IR_REMOTE_LINK_DEBUG_PRINT(HANDLE, FUC)       (HANDLE)->debug_print = FUC

/**
 * @brief     link receive_callback function
 * @param[in] HANDLE points to an ir_remote handle structure
 * @param[in] FUC points to a receive_callback function address
 * @note      none
 */
#define DRIVER_IR_REMOTE_LINK_RECEIVE_CALLBACK(HANDLE, FUC)  (HANDLE)->receive_callback = FUC


/**
 * @brief      get chip's information
 * @param[out] *info points to an ir_remote info structure
 * @return     status code
 *             - 0 success
 *             - 2 handle is NULL
 * @note       none
 */
uint8_t ir_remote_info(ir_remote_info_t *info);

/**
 * @brief     irq handler
 * @param[in] *handle points to an ir_remote handle structure
 * @return    status code
 *            - 0 success
 *            - 1 run failed
 *            - 2 handle is NULL
 *            - 3 handle is not initialized
 * @note      none
 */
uint8_t ir_remote_irq_handler(ir_remote_handle_t *handle);

/**
 * @brief     initialize the chip
 * @param[in] *handle points to an ir_remote handle structure
 * @return    status code
 *            - 0 success
 *            - 1 gpio initialization failed
 *            - 2 handle is NULL
 *            - 3 linked functions is NULL
 * @note      none
 */
uint8_t ir_remote_init(ir_remote_handle_t *handle);

/**
 * @brief     close the chip
 * @param[in] *handle points to an ir_remote handle structure
 * @return    status code
 *            - 0 success
 *            - 2 handle is NULL
 *            - 3 handle is not initialized
 * @note      none
 */
uint8_t ir_remote_deinit(ir_remote_handle_t *handle);

#ifdef __cplusplus
}
#endif

#endif

driver_ir_remote.c

#include "driver_ir_remote.h"

#define CHIP_NAME                           "NEC IR REMOTE" /**< chip name */
#define MANUFACTURER_NAME                   "NEC"           /**< manufacturer name */
#define SUPPLY_VOLTAGE_MIN                  2.7f            /**< chip min supply voltage */
#define SUPPLY_VOLTAGE_MAX                  5.5f            /**< chip max supply voltage */
#define MAX_CURRENT                         1.5f            /**< chip max current */
#define TEMPERATURE_MIN                     -40.0f          /**< chip min operating temperature */
#define TEMPERATURE_MAX                     125.0f          /**< chip max operating temperature */
#define DRIVER_VERSION                      1000            /**< driver version */

#define IR_REMOTE_CHECK_START_HIGH          9000            /**< start frame high time */
#define IR_REMOTE_CHECK_START_LOW           4500            /**< start frame low time */
#define IR_REMOTE_CHECK_DATA0_HIGH          562             /**< bit 0 frame high time */
#define IR_REMOTE_CHECK_DATA0_LOW           562             /**< bit 0 frame low time */
#define IR_REMOTE_CHECK_DATA1_HIGH          562             /**< bit 1 frame high time */
#define IR_REMOTE_CHECK_DATA1_LOW           1687            /**< bit 1 frame low time */
#define IR_REMOTE_CHECK_DATA_0_1_EDGE       1100            /**< data 0 and 1 edge */
#define IR_REMOTE_CHECK_STOP                562             /**< stop time */
#define IR_REMOTE_CHECK_REPEAT              2250            /**< repeat time */

/**
 * @brief     check the frame time
 * @param[in] check is the checked time
 * @param[in] t is the standard time
 * @return    status code
 *            - 0 success
 *            - 1 checked failed
 * @note      none
 */
static inline uint8_t a_check_frame(uint16_t check, uint16_t t)
{
    /* 
    Check whether the time length tolerance of each pulse part in one frame data exceeds the IR_REMOTE_MAX_RANGE 
    (maximum error range) of the standard time length of each part specified in the NEC protocol 
    */
    if (abs(check - t) > (int)((float)(t) * IR_REMOTE_MAX_RANGE))        /* check the time */
    {
        return 1;                                                        /* check failed */
    }
    else
    {
        return 0;                                                        /* success return 0 */
    }
}

/**
 * @brief     ir_remote nec repeat decode
 * @param[in] *handle points to an ir_remote handle structure
 * @note      none
 *
 * 参考 ir_remote_nec_decode 的详细注释
 *
 */
static void a_ir_remote_nec_repeat_decode(ir_remote_handle_t *handle)
{
    uint16_t i;
    uint16_t len;
    ir_remote_t data;
    
    len = handle->decode_len - 1;                                                         /* len - 1 */
    for (i = 0; i < len; i++)                                                             /* diff all time */
    {
        int64_t diff;
        
        diff = (int64_t)((int64_t)handle->decode[i + 1].t.s -
               (int64_t)handle->decode[i].t.s) * 1000000 + 
               (int64_t)((int64_t)handle->decode[i + 1].t.us -
               (int64_t)handle->decode[i].t.us);                                          /* diff time */
        handle->decode[i].diff_us = (uint32_t)diff;                                       /* save the time diff */
    }
    
    if (a_check_frame(handle->decode[0].diff_us, IR_REMOTE_CHECK_START_HIGH) != 0)        /* check start diff */
    {
        if (handle->receive_callback != NULL)                                             /* check the receive callback */
        {
            data.address = 0x00;                                                          /* set address 0x00 */
            data.command = 0x00;                                                          /* set command 0x00 */
            data.status = IR_REMOTE_STATUS_FRAME_INVALID;                                 /* frame invalid */
            handle->receive_callback(&data);                                              /* run the callback */
        }
        
        return;                                                                           /* return */
    }
    if (a_check_frame(handle->decode[1].diff_us, IR_REMOTE_CHECK_REPEAT) != 0)            /* check repeat */
    {
        if (handle->receive_callback != NULL)                                             /* check the receive callback */
        {
            data.address = 0x00;                                                          /* set address 0x00 */
            data.command = 0x00;                                                          /* set command 0x00 */
            data.status = IR_REMOTE_STATUS_FRAME_INVALID;                                 /* frame invalid */
            handle->receive_callback(&data);                                              /* run the callback */
        }
        
        return;                                                                           /* return */
    }
    if (a_check_frame(handle->decode[2].diff_us, IR_REMOTE_CHECK_STOP) != 0)              /* check stop */
    {
        if (handle->receive_callback != NULL)                                             /* check the receive callback */
        {
            data.address = 0x00;                                                          /* set address 0x00 */
            data.command = 0x00;                                                          /* set command 0x00 */
            data.status = IR_REMOTE_STATUS_FRAME_INVALID;                                 /* frame invalid */
            handle->receive_callback(&data);                                              /* run the callback */
        }
        
        return;                                                                           /* return */
    }
    
    if (handle->receive_callback != NULL)                                                 /* check the receive callback */
    {
        data.address = handle->last_code.address;                                         /* set address 0x00 */
        data.command = handle->last_code.command;                                         /* set command 0x00 */
        data.status = IR_REMOTE_STATUS_REPEAT;                                            /* frame invalid */
        handle->receive_callback(&data);                                                  /* run the callback */
    }
}

/**
 * @brief     ir_remote nec decode
 * @param[in] *handle points to an ir_remote handle structure
 * @note      none
 */
static void a_ir_remote_nec_decode(ir_remote_handle_t *handle)
{
    uint8_t tmp;        /* 临时存储解析出的数据 */
    uint8_t tmp_r;      /* 临时存储解析出的数据反码 */
    uint8_t tmp_cmp;    /* 临时存储反码的反码用于和原码比较是否一致,检验使用 */
    uint16_t i;
    uint16_t len;
    ir_remote_t data;

    /* 计算脉冲序列中相邻脉冲之间时间差的个数。 */
    /* 如果脉冲序列中有 n 个脉冲,那么相邻脉冲之间的时间差就有 n-1 个,len 确保后面的循环计算时间差时不会越界 */
    len = handle->decode_len - 1; /* len - 1 */

    /* 一帧信息中相邻两个脉冲之间的时间差计算,即用来计算一个脉冲的时长 */
    /* 时间差数据将用于后续解析地址码和命令码 */
    for (i = 0; i < len; i++) /* diff all time */
    {
        int64_t diff;
        
        diff = (int64_t)((int64_t)handle->decode[i + 1].t.s -
               (int64_t)handle->decode[i].t.s) * 1000000 + 
               (int64_t)((int64_t)handle->decode[i + 1].t.us -
               (int64_t)handle->decode[i].t.us);                                                              /* diff time */
        handle->decode[i].diff_us = (uint32_t)diff;                                                           /* save the time diff */
    }

    /* 检查起始位的高频信号部分的时间是否满足 9ms(9000μs)的要求 */
    if (a_check_frame(handle->decode[0].diff_us, IR_REMOTE_CHECK_START_HIGH) != 0)                            /* check start diff */
    {   
        /* 错误则会执行回调,给出错误信息 */
        if (handle->receive_callback != NULL)                                                                 /* check the receive callback */
        {
            data.address = 0x00;                          /* set address 0x00 */
            data.command = 0x00;                          /* set command 0x00 */
            data.status = IR_REMOTE_STATUS_FRAME_INVALID; /* frame invalid */
            handle->receive_callback(&data);              /* run the callback */
        }

        return; /* return */
    }

    /* 检查起始位的低电平部分的时间是否满足 4.5ms(4500μs)的要求 */
    if (a_check_frame(handle->decode[1].diff_us, IR_REMOTE_CHECK_START_LOW) != 0) /* check start low */
    {
        if (handle->receive_callback != NULL) /* check the receive callback */
        {
            data.address = 0x00;                          /* set address 0x00 */
            data.command = 0x00;                          /* set command 0x00 */
            data.status = IR_REMOTE_STATUS_FRAME_INVALID; /* frame invalid */
            handle->receive_callback(&data);              /* run the callback */
        }

        return; /* return */
    }

    /* 临时存储解析出的地址码 */
    tmp = 0; /* init 0 */
    /* 检查 8 位地址码的每一个逻辑位的高频信号部分的时间和低电平部分信号的时间是否满足要求 */
    for (i = 0; i < 8; i++) /* parse 8 bit */
    {
        /* 略过起始位的高频脉冲部分和低电平信号部分从地址码的高频信号部分开始,检查每个逻辑位的高频信号部分的时间是否满足要求 */
        if (a_check_frame(handle->decode[2 + i * 2 + 0].diff_us, IR_REMOTE_CHECK_DATA1_HIGH) != 0) /* check data high */
        {
            if (handle->receive_callback != NULL) /* check the receive callback */
            {
                data.address = 0x00;                     /* set address 0x00 */
                data.command = 0x00;                     /* set command 0x00 */
                data.status = IR_REMOTE_STATUS_ADDR_ERR; /* address error */
                handle->receive_callback(&data);         /* run the callback */
            }

            return; /* return */
        }

        /* 略过起始位的高频脉冲部分和低电平信号部分从地址码的高频信号部分开始,检查每个逻辑位的低电平部分时长来区分是逻辑 "0" 还是 逻辑 "1" */
        /* 如果每个逻辑位的低电平信号时长大于 IR_REMOTE_CHECK_DATA_0_1_EDGE 规定的长度,可以判断为逻辑 "1",反之为逻辑 "0" */
        /* 其中 IR_REMOTE_CHECK_DATA_0_1_EDGE 本人取了逻辑 "0" 和 逻辑 "1" 规定的标准时长的中位数 (562 + 1687) / 2 */
        if (handle->decode[2 + i * 2 + 1].diff_us > IR_REMOTE_CHECK_DATA_0_1_EDGE) /* check data0 and data1 */
        {
            /* 如果是逻辑 "1",检查逻辑 "1" 的低电平部分的时间是否满足要求*/
            if (a_check_frame(handle->decode[2 + i * 2 + 1].diff_us, IR_REMOTE_CHECK_DATA1_LOW) != 0) /* check data 1 */
            {
                data.address = 0x00;                     /* set address 0x00 */
                data.command = 0x00;                     /* set command 0x00 */
                data.status = IR_REMOTE_STATUS_ADDR_ERR; /* address error */
                if (handle->receive_callback != NULL)    /* check the receive callback */
                {
                    handle->receive_callback(&data); /* run the callback */
                }

                return; /* return */
            }
            /* NEC协议 按照低位在前,高位在后的顺序发送(LSB),先接收到的是低位数据,后接受到的数据依次左移到高位 */
            tmp |= 1 << i; /* set bit */
        }
        else /* check data 0 */
        {
            /* 如果是逻辑 "0",检查逻辑 "0" 的低电平部分的时间是否满足要求*/
            if (a_check_frame(handle->decode[2 + i * 2 + 1].diff_us, IR_REMOTE_CHECK_DATA0_LOW) != 0) /* check data 0 */
            {
                data.address = 0x00;                     /* set address 0x00 */
                data.command = 0x00;                     /* set command 0x00 */
                data.status = IR_REMOTE_STATUS_ADDR_ERR; /* address error */
                if (handle->receive_callback != NULL)    /* check the receive callback */
                {
                    handle->receive_callback(&data); /* run the callback */
                }

                return; /* return */
            }
            /* 按照低位在前,高位在后的顺序发送(LSB),先接收到的是低位数据,后接受到的数据依次左移到高位 */
            tmp |= 0 << i; /* set bit */
        }
    }

    /* 临时存储解析出的地址反码 */
    tmp_r = 0; /* init 0 */
    /* 检查 8 位地址反码的每一个逻辑位的高频信号部分的时间和低电平部分信号的时间是否满足要求 */
    for (i = 0; i < 8; i++) /* parse 8 bit */
    {
        /* 略过起始位和地址码的部分从地址反码的高频信号部分开始,检查每个逻辑位的高频信号部分的时间是否满足要求 */
        if (a_check_frame(handle->decode[18 + i * 2 + 0].diff_us, IR_REMOTE_CHECK_DATA1_HIGH) != 0) /* check data high */
        {
            if (handle->receive_callback != NULL) /* check the receive callback */
            {
                data.address = 0x00;                     /* set address 0x00 */
                data.command = 0x00;                     /* set command 0x00 */
                data.status = IR_REMOTE_STATUS_ADDR_ERR; /* address error */
                handle->receive_callback(&data);         /* run the callback */
            }

            return; /* return */
        }

        /* 略过起始位和地址码的部分从地址码的反码的高频信号部分开始,检查每个逻辑位的低电平部分时长来区分是逻辑 "0" 还是 逻辑 "1" */
        /* 如果每个逻辑位的低电平信号时长大于 IR_REMOTE_CHECK_DATA_0_1_EDGE 规定的长度,可以判断为逻辑 "1",反之为逻辑 "0" */
        /* 其中 IR_REMOTE_CHECK_DATA_0_1_EDGE 我取了逻辑 "0" 和 逻辑 "1" 规定的标准时长的中位数 (562 + 1687) / 2 */
        if (handle->decode[18 + i * 2 + 1].diff_us > IR_REMOTE_CHECK_DATA_0_1_EDGE) /* check data0 and data1 */
        {
            /* 如果是逻辑 "1",检查逻辑 "1" 的低电平部分的时间是否满足要求*/
            if (a_check_frame(handle->decode[18 + i * 2 + 1].diff_us, IR_REMOTE_CHECK_DATA1_LOW) != 0) /* check data 1 */
            {
                data.address = 0x00;                     /* set address 0x00 */
                data.command = 0x00;                     /* set command 0x00 */
                data.status = IR_REMOTE_STATUS_ADDR_ERR; /* address error */
                if (handle->receive_callback != NULL)    /* check the receive callback */
                {
                    handle->receive_callback(&data); /* run the callback */
                }

                return; /* return */
            }
            /* 按照低位在前,高位在后的顺序发送(LSB),先接收到的是低位数据,后接受到的数据依次左移到高位 */
            tmp_r |= 1 << i; /* set bit */
        }
        else /* check data 0 */
        {
            /* 如果是逻辑 "0",检查逻辑 "0" 的低电平部分的时间是否满足要求*/
            if (a_check_frame(handle->decode[18 + i * 2 + 1].diff_us, IR_REMOTE_CHECK_DATA0_LOW) != 0) /* check data 0 */
            {
                data.address = 0x00;                     /* set address 0x00 */
                data.command = 0x00;                     /* set command 0x00 */
                data.status = IR_REMOTE_STATUS_ADDR_ERR; /* address error */
                if (handle->receive_callback != NULL)    /* check the receive callback */
                {
                    handle->receive_callback(&data); /* run the callback */
                }

                return; /* return */
            }
            /* 按照低位在前,高位在后的顺序发送(LSB),先接收到的是低位数据,后接受到的数据依次左移到高位 */
            tmp_r |= 0 << i; /* set bit */
        }
    }

    /* tmp_cmp 存储解析出的地址反码的反码 */
    tmp_cmp = ~tmp_r; /* get the check value */

    /* tmp_cmp 与 tmp 比较,校验地址码是否正确 */
    if (tmp != tmp_cmp) /* check the value */
    {
        data.address = 0x00;                     /* set address 0x00 */
        data.command = 0x00;                     /* set command 0x00 */
        data.status = IR_REMOTE_STATUS_ADDR_ERR; /* address error */
        if (handle->receive_callback != NULL)    /* check the receive callback */
        {
            handle->receive_callback(&data); /* run the callback */
        }

        return; /* return */
    }
    data.address = tmp; /* set the address */

    /* 临时存储解析出的命令码 */
    tmp = 0;                /* init 0 */
    for (i = 0; i < 8; i++) /* parse 8 bit */
    {
        if (a_check_frame(handle->decode[34 + i * 2 + 0].diff_us, IR_REMOTE_CHECK_DATA1_HIGH) != 0) /* check data high */
        {
            if (handle->receive_callback != NULL) /* check the receive callback */
            {
                data.command = 0x00;                    /* set command 0x00 */
                data.status = IR_REMOTE_STATUS_CMD_ERR; /* command error */
                handle->receive_callback(&data);        /* run the callback */
            }

            return; /* return */
        }
        if (handle->decode[34 + i * 2 + 1].diff_us > IR_REMOTE_CHECK_DATA_0_1_EDGE) /* check data0 and data1 */
        {
            if (a_check_frame(handle->decode[34 + i * 2 + 1].diff_us, IR_REMOTE_CHECK_DATA1_LOW) != 0) /* check data 1 */
            {
                data.command = 0x00;                    /* set command 0x00 */
                data.status = IR_REMOTE_STATUS_CMD_ERR; /* command error */
                if (handle->receive_callback != NULL)   /* check the receive callback */
                {
                    handle->receive_callback(&data); /* run the callback */
                }

                return; /* return */
            }
            tmp |= 1 << i; /* set bit */
        }
        else /* check data 0 */
        {
            if (a_check_frame(handle->decode[34 + i * 2 + 1].diff_us, IR_REMOTE_CHECK_DATA0_LOW) != 0) /* check data 0 */
            {
                data.command = 0x00;                    /* set command 0x00 */
                data.status = IR_REMOTE_STATUS_CMD_ERR; /* command error */
                if (handle->receive_callback != NULL)   /* check the receive callback */
                {
                    handle->receive_callback(&data); /* run the callback */
                }

                return; /* return */
            }
            tmp |= 0 << i; /* set bit */
        }
    }

    /* 临时存储解析出的命令码的反码 */
    tmp_r = 0;              /* init 0 */
    for (i = 0; i < 8; i++) /* parse 8 bit */
    {
        if (a_check_frame(handle->decode[50 + i * 2 + 0].diff_us, IR_REMOTE_CHECK_DATA1_HIGH) != 0) /* check data high */
        {
            if (handle->receive_callback != NULL) /* check the receive callback */
            {
                data.command = 0x00;                    /* set command 0x00 */
                data.status = IR_REMOTE_STATUS_CMD_ERR; /* command error */
                handle->receive_callback(&data);        /* run the callback */
            }

            return; /* return */
        }
        if (handle->decode[50 + i * 2 + 1].diff_us > IR_REMOTE_CHECK_DATA_0_1_EDGE) /* check data0 and data1 */
        {
            if (a_check_frame(handle->decode[50 + i * 2 + 1].diff_us, IR_REMOTE_CHECK_DATA1_LOW) != 0) /* check data 1 */
            {
                data.command = 0x00;                    /* set command 0x00 */
                data.status = IR_REMOTE_STATUS_CMD_ERR; /* command error */
                if (handle->receive_callback != NULL)   /* check the receive callback */
                {
                    handle->receive_callback(&data); /* run the callback */
                }

                return; /* return */
            }
            tmp_r |= 1 << i; /* set bit */
        }
        else /* check data 0 */
        {
            if (a_check_frame(handle->decode[50 + i * 2 + 1].diff_us, IR_REMOTE_CHECK_DATA0_LOW) != 0) /* check data 0 */
            {
                data.command = 0x00;                    /* set command 0x00 */
                data.status = IR_REMOTE_STATUS_CMD_ERR; /* command error */
                if (handle->receive_callback != NULL)   /* check the receive callback */
                {
                    handle->receive_callback(&data); /* run the callback */
                }

                return; /* return */
            }
            tmp_r |= 0 << i; /* set bit */
        }
    }

    /* tmp_cmp 存储解析出的命令反码的反码 */
    tmp_cmp = ~tmp_r;   /* get the check value */
    if (tmp != tmp_cmp) /* check the value */
    {
        data.command = 0x00;                    /* set command 0x00 */
        data.status = IR_REMOTE_STATUS_CMD_ERR; /* command error */
        if (handle->receive_callback != NULL)   /* check the receive callback */
        {
            handle->receive_callback(&data); /* run the callback */
        }

        return; /* return */
    }
    data.command = tmp; /* set the command */

    /* 检查停止位的高频信号部分的时间是否满足要求 */
    if (a_check_frame(handle->decode[66].diff_us, IR_REMOTE_CHECK_STOP) != 0) /* check stop frame */
    {
        if (handle->receive_callback != NULL) /* check the receive callback */
        {
            data.status = IR_REMOTE_STATUS_FRAME_INVALID; /* frame invalid */
            handle->receive_callback(&data);              /* run the callback */
        }

        return; /* return */
    }

    if (handle->receive_callback != NULL) /* check the receive callback */
    {
        data.status = IR_REMOTE_STATUS_OK; /* frame ok */
        handle->receive_callback(&data);   /* run the callback */
    }
    handle->last_code.address = data.address; /* save address */
    handle->last_code.command = data.command; /* save command */
    handle->last_code.status = data.status;   /* save status */
    handle->decode_len = 0;                   /* clear the buffer */
}

/**
 * @brief     irq handler
 * @param[in] *handle points to an ir_remote handle structure
 * @return    status code
 *            - 0 success
 *            - 1 run failed
 *            - 2 handle is NULL
 *            - 3 handle is not initialized
 * @note      none
 *
 * 红外接收中断处理(服务)函数,负责处理红外信号的接收和解码
 *
 * 产生外部中断的引脚内部是上拉,在无操作的情况下红外接收器的内部电路在引脚处是高阻抗状态,并会将 38kHz 的高频信号转换成低电平
 * 这样红外接收器在收到第一个数据帧的起始位的 9ms 高频信号时内部电路导通将引脚拉低,MCU 读到引脚产生下降沿则会触发外部中断,执行如下的中断回调函数
 * 并在 9ms 高频信号后内部电路断开再次将引脚拉高变为高阻态,MCU 读到引脚产生上升沿则会再次触发外部中断,执行如下的中断回调函数
 * 如此循环直到接收到一帧数据
 *
 */
// uint8_t i = 0;
uint8_t ir_remote_irq_handler(ir_remote_handle_t *handle)
{
    uint8_t res;
    int32_t diff;
    ir_remote_time_t t;

    if (handle == NULL) /* check handle */
    {
        return 2; /* return error */
    }
    if (handle->inited != 1) /* check handle initialization */
    {
        return 3; /* return error */
    }

    /* 获取发生中断时的当前时间的时间戳 */
    res = handle->timestamp_read(&t); /* timestamp read */

    if (res != 0)                     /* check result */
    {
        handle->debug_print("ir_remote: timestamp read failed.\n"); /* timestamp read failed */

        return 1; /* return error */
    }

    /* 计算时间差,并重置解码器 */
    /* 计算当前时间戳 t 与上一次时间戳 last_time 的时间差 diff(单位:微秒) */
    /* 对于第一次中断,这个时间差 diff 是当前中断发生时读取到的时间戳与初始化时读取到的时间戳的差值,同理,之后都是两次中断之间时间戳的差值 */
    diff = (int64_t)(t.s - handle->last_time.s) * 1000000 + 
           (int64_t)(t.us - handle->last_time.us); /* now - last time */

    /* 每按下一次按键,红外遥控器的红外发射二极管会发射一帧数据 */
    /* diff 记录了两次中断,即这次脉冲开始的的时间戳和上次脉冲开始的时间戳之间的时间差,表示上次脉冲开始后到这个脉冲的总时长 */
    /* 一帧数据的发射时长为 67.5ms(67500μs) 假设我以迅雷不及掩耳之势连着按了两下,那么两帧数据之间的时间差值至少也有 67500μs */
    /* 对于重复帧,在发送完一帧数据后的 40ms(40000μs) 左右会连续发送 108ms(108000μs) 时间间隔的重复帧,以最长时间的重复帧为例 */
    /* 如果 diff 的值大于一个重复帧的时长,那说明红外发射管在发送完上次的一帧数据后就不再发送,再次发送就可以丢弃掉之前已经发送过的数据帧 */
    if (diff - (int64_t)200000L >= 0) /* if over 1s, force reset */
    {
        handle->decode_len = 0; /* reset the decode */
    }
    /* 超过缓冲区的最大长度 */
    if (handle->decode_len >= IR_REMOTE_DECODE_BUF_MAX_LEN - 1) /* check the max length */
    {
        handle->decode_len = 0; /* reset the decode */
    }

    /* 保存当前读取到的时间戳和更新解码索引长度 */
    handle->decode[handle->decode_len].t.s = t.s;   /* save s */
    handle->decode[handle->decode_len].t.us = t.us; /* save us */
    handle->decode_len++;                           /* length++ */
    handle->last_time.s = t.s;                      /* save last time */
    handle->last_time.us = t.us;                    /* save last time */

    /* 检查解码长度并解码 */
    /* 先尝试解码数据帧 */
    /* 一个完整的数据帧其脉冲序列的长度应该是 67,在收到数据帧的最后一个脉冲时,decode_len 为 68 */
    if (handle->decode_len >= 68) /* check the end length */
    {
        /* 检查该帧是否为数据帧,数据帧与重复帧的最大区别是,第二个脉冲的时长不同,数据帧是 4.5 ms 的低电平间隔 */
        diff = (int64_t)((int64_t)handle->decode[2].t.s -
                         (int64_t)handle->decode[1].t.s) * 1000000 + 
               (int64_t)((int64_t)handle->decode[2].t.us -
                         (int64_t)handle->decode[1].t.us);                 /* diff time */
        if (a_check_frame((uint16_t)diff, IR_REMOTE_CHECK_START_LOW) == 0) /* check the frame */
        {
            /* 满足数据帧,进行数据帧解码 */
            a_ir_remote_nec_decode(handle); /* try to decode */
        }
    }

    /* 一个完整的重复帧其脉冲序列的长度应该是 3,如果收到的该帧不是数据帧,则按收到重复帧的最后一个脉冲时,decode_len 为 4 */
    if (handle->decode_len == 4) /* check the end length */
    {
        /* 检查该帧是否为重复帧,数据帧与重复帧的最大区别是,第二个脉冲的时长不同,重复帧是 2.25 ms 的低电平间隔 */
        diff = (int64_t)((int64_t)handle->decode[2].t.s - 
                         (int64_t)handle->decode[1].t.s) * 1000000 +
               (int64_t)((int64_t)handle->decode[2].t.us -
                         (int64_t)handle->decode[1].t.us);              /* diff time */
        if (a_check_frame((uint16_t)diff, IR_REMOTE_CHECK_REPEAT) == 0) /* check the frame */
        {
            /* 满足重复帧,进行重复帧解码 */
            a_ir_remote_nec_repeat_decode(handle); /* try to decode */
        }
    }

    return 0; /* success return 0 */
}

/**
 * @brief     initialize the chip
 * @param[in] *handle points to an ir_remote handle structure
 * @return    status code
 *            - 0 success
 *            - 1 gpio initialization failed
 *            - 2 handle is NULL
 *            - 3 linked functions is NULL
 * @note      none
 */
uint8_t ir_remote_init(ir_remote_handle_t *handle)
{
    uint8_t res;
    ir_remote_time_t t;

    if (handle == NULL) /* check handle */
    {
        return 2; /* return error */
    }
    if (handle->debug_print == NULL) /* check debug_print */
    {
        return 3; /* return error */
    }
    if (handle->timestamp_read == NULL) /* check timestamp_read */
    {
        handle->debug_print("ir_remote: timestamp_read is null.\n"); /* timestamp_read is null */

        return 3; /* return error */
    }
    if (handle->delay_ms == NULL) /* check delay_ms */
    {
        handle->debug_print("ir_remote: delay_ms is null.\n"); /* delay_ms is null */

        return 3; /* return error */
    }
    if (handle->receive_callback == NULL) /* check receive_callback */
    {
        handle->debug_print("ir_remote: receive_callback is null.\n"); /* receive_callback is null */

        return 3; /* return error */
    }

    /* 读取初始化时的当前时间的时间戳 */
    res = handle->timestamp_read(&t); /* timestamp read */
    if (res != 0)                     /* check result */
    {
        handle->debug_print("ir_remote: timestamp read failed.\n"); /* timestamp read failed */

        return 1; /* return error */
    }

    handle->last_time.s = t.s;        /* save last time */
    handle->last_time.us = t.us;      /* save last time */
    handle->last_code.address = 0x00; /* init address 0 */
    handle->last_code.command = 0x00; /* init command 0 */
    handle->last_code.status = 0x00;  /* init status 0 */
    handle->decode_len = 0;           /* init 0 */
    handle->inited = 1;               /* flag inited */

    return 0; /* success return 0 */
}

/**
 * @brief     close the chip
 * @param[in] *handle points to an ir_remote handle structure
 * @return    status code
 *            - 0 success
 *            - 2 handle is NULL
 *            - 3 handle is not initialized
 * @note      none
 */
uint8_t ir_remote_deinit(ir_remote_handle_t *handle)
{
    if (handle == NULL) /* check handle */
    {
        return 2; /* return error */
    }
    if (handle->inited != 1) /* check handle initialization */
    {
        return 3; /* return error */
    }

    handle->inited = 0; /* flag close */

    return 0; /* success return 0 */
}

/**
 * @brief      get chip's information
 * @param[out] *info points to an ir_remote info structure
 * @return     status code
 *             - 0 success
 *             - 2 handle is NULL
 * @note       none
 */
uint8_t ir_remote_info(ir_remote_info_t *info)
{
    if (info == NULL) /* check handle */
    {
        return 2; /* return error */
    }

    memset(info, 0, sizeof(ir_remote_info_t));               /* initialize ir_remote info structure */
    strncpy(info->chip_name, CHIP_NAME, 32);                 /* copy chip name */
    strncpy(info->manufacturer_name, MANUFACTURER_NAME, 32); /* copy manufacturer name */
    strncpy(info->interface, "GPIO", 8);                     /* copy interface name */
    info->supply_voltage_min_v = SUPPLY_VOLTAGE_MIN;         /* set minimal supply voltage */
    info->supply_voltage_max_v = SUPPLY_VOLTAGE_MAX;         /* set maximum supply voltage */
    info->max_current_ma = MAX_CURRENT;                      /* set maximum current */
    info->temperature_max = TEMPERATURE_MAX;                 /* set minimal temperature */
    info->temperature_min = TEMPERATURE_MIN;                 /* set maximum temperature */
    info->driver_version = DRIVER_VERSION;                   /* set driver version */

    return 0; /* success return 0 */
}

driver_ir_remote_send.h

#ifndef __DRIVER_IR_REMOTE_SEND_H__
#define __DRIVER_IR_REMOTE_SEND_H__

#include "main.h"
#include "driver_ir_remote.h"

#ifdef __cplusplus
extern "C" {
#endif

uint8_t ir_remote_send_init(TIM_HandleTypeDef *ir_remote_send_tim, uint32_t tim_pwm_channel);

uint8_t ir_remote_send_deinit(TIM_HandleTypeDef *ir_remote_send_tim);

uint8_t ir_remote_send_frame_data(ir_remote_t *data);

uint8_t ir_remote_send_frame_repeat(void);

#ifdef __cplusplus
}
#endif

#endif

driver_ir_remote_send.c

#include "driver_ir_remote_send.h"
#include "driver_ir_remote.h"
#include "delay.h"

#define NEC_START_HIGH_US       9000    // 起始位高频脉冲持续时间 (9ms)
#define NEC_START_LOW_US        4500    // 起始位低电平持续时间 (4.5ms)
#define NEC_DATA_HIGH_US        562     // 数据位高频脉冲持续时间 (560µs)
#define NEC_DATA_LOW_0_US       562     // 数据位低电平持续时间 (逻辑0: 560µs)
#define NEC_DATA_LOW_1_US       1687    // 数据位低电平持续时间 (逻辑1: 1690µs)
#define NEC_STOP_HIGH_US        562     // 停止位高频脉冲持续时间 (560µs)
#define NEC_REPEAT_LOW_US       2250    // 重复位低电平持续时间 (2.25ms)
#define NEC_END_HIGH_US         562     // 停止位高频脉冲持续时间 (9ms)
#define NEC_REPEAT_GAP_US       40000   // 重复位间隔时间 (40ms)

/**
 * @brief
 */
static TIM_HandleTypeDef ir_remote_send_tim_handle;

static uint32_t ir_remote_send_tim_pwm_channel; 


uint8_t ir_remote_send_init(TIM_HandleTypeDef *ir_remote_send_tim, uint32_t tim_pwm_channel)
{
    if (ir_remote_send_tim == NULL)
    {
        return 2;
    }
    if (HAL_TIM_Base_DeInit(&ir_remote_send_tim_handle) != HAL_OK)
    {
        return 3;
    }

    TIM_OC_InitTypeDef sConfigOC;

    ir_remote_send_tim_handle.Instance = ir_remote_send_tim->Instance;
    ir_remote_send_tim_handle.Init.Prescaler = SystemCoreClock / 1000000 - 1;
    ir_remote_send_tim_handle.Init.CounterMode = TIM_COUNTERMODE_UP;
    ir_remote_send_tim_handle.Init.Period = 26 - 1;
    ir_remote_send_tim_handle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    ir_remote_send_tim_handle.Init.RepetitionCounter = 0;
    ir_remote_send_tim_handle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;

    if (HAL_TIM_PWM_Init(&ir_remote_send_tim_handle) != HAL_OK)
    {
        return 1;
    }

    // 配置 PWM 输出通道
    ir_remote_send_tim_pwm_channel = tim_pwm_channel;

    sConfigOC.OCMode = TIM_OCMODE_PWM1;
    sConfigOC.Pulse = 13;                     // 占空比:50%(26 / 2) 占空比:25%(13 / 2)
    sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
    sConfigOC.OCFastMode = TIM_OCFAST_ENABLE;
    HAL_TIM_PWM_ConfigChannel(&ir_remote_send_tim_handle, &sConfigOC, TIM_CHANNEL_2);

    // 启动 PWM 输出
    // HAL_TIM_PWM_Start(&ir_remote_send_tim_handle, TIM_CHANNEL_2);

    return 0;
}

uint8_t ir_remote_send_deinit(TIM_HandleTypeDef *ir_remote_send_tim)
{
    if (ir_remote_send_tim == NULL)
    {
        return 2;
    }
    if (HAL_TIM_Base_DeInit(ir_remote_send_tim) != HAL_OK)
    {
        return 3;
    }

    return 0;
}

static void ir_remote_send_bit(uint8_t bit)
{
    if (bit) // 发送逻辑 1
    {
        /* 为了方便移植,这里可以改为调用自己定义的接口 */
        HAL_TIM_PWM_Start(&ir_remote_send_tim_handle, ir_remote_send_tim_pwm_channel); // 开启 38kHz
        delay_us(NEC_DATA_HIGH_US);
        HAL_TIM_PWM_Stop(&ir_remote_send_tim_handle, ir_remote_send_tim_pwm_channel);  // 关闭 38kHz
        delay_us(NEC_DATA_LOW_1_US);
    }
    else // 发送逻辑 0
    {
        HAL_TIM_PWM_Start(&ir_remote_send_tim_handle, ir_remote_send_tim_pwm_channel); // 开启 38kHz
        delay_us(NEC_DATA_HIGH_US);                             // 高频脉冲持续 562.5μs
        HAL_TIM_PWM_Stop(&ir_remote_send_tim_handle, ir_remote_send_tim_pwm_channel);  // 关闭 38kHz
        delay_us(NEC_DATA_LOW_0_US);                             // 低电平持续 562.5μs
    }
}

uint8_t ir_remote_send_frame_data(ir_remote_t *data)
{
    uint8_t i;
    uint32_t frame;

    frame |= (data->address & 0xFF);             // 地址
    frame |= ((~(data->address) & 0xFF) << 8);   // 地址反码
    frame |= (data->command & 0xFF) << 16;       // 命令
    frame |= ((~(data->command) & 0xFF) << 24);  // 命令反码

    // 发送起始信号
    HAL_TIM_PWM_Start(&ir_remote_send_tim_handle, ir_remote_send_tim_pwm_channel); // 开启 38kHz
    delay_us(NEC_START_HIGH_US);                             // 起始高频脉冲持续 9ms
    HAL_TIM_PWM_Stop(&ir_remote_send_tim_handle, ir_remote_send_tim_pwm_channel);  // 关闭 38kHz
    delay_us(NEC_START_LOW_US);                             // 起始低电平持续 4.5ms

    // 发送 32 位数据(LSB)
    for (i = 0; i < 32; i++)
    {
        ir_remote_send_bit((frame >> i) & 0x01); // 按位发送
    }

    // 发送停止信号
    HAL_TIM_PWM_Start(&ir_remote_send_tim_handle, ir_remote_send_tim_pwm_channel); // 开启 38kHz
    delay_us(NEC_END_HIGH_US);                             // 停止位高频脉冲持续 562.5μs
    HAL_TIM_PWM_Stop(&ir_remote_send_tim_handle, ir_remote_send_tim_pwm_channel);  // 关闭 38kHz

    return 0;
}

uint8_t ir_remote_send_frame_repeat(void)
{
    // 发送起始位
    HAL_TIM_PWM_Start(&ir_remote_send_tim_handle, ir_remote_send_tim_pwm_channel); // 开启 38kHz
    delay_us(NEC_START_HIGH_US);
    HAL_TIM_PWM_Stop(&ir_remote_send_tim_handle, ir_remote_send_tim_pwm_channel);  // 关闭 38kHz
    
    delay_us(NEC_REPEAT_LOW_US);

    // 发送停止位
    HAL_TIM_PWM_Start(&ir_remote_send_tim_handle, ir_remote_send_tim_pwm_channel); // 开启 38kHz
    delay_us(NEC_END_HIGH_US);                             // 停止位高频脉冲持续 562.5μs
    HAL_TIM_PWM_Stop(&ir_remote_send_tim_handle, ir_remote_send_tim_pwm_channel);  // 关闭 38kHz

    delay_us(NEC_REPEAT_GAP_US);

    return 0;
}

driver_ir_remote_interface.h

#ifndef __DRIVER_IR_REMOTE_INTERFACE_H__
#define __DRIVER_IR_REMOTE_INTERFACE_H__

#include "driver_ir_remote.h"

#ifdef __cplusplus
extern "C"{
#endif

/**
 * @defgroup ir_remote_interface_driver ir_remote interface driver function
 * @brief    ir_remote interface driver modules
 * @ingroup  ir_remote_driver
 * @{
 */

/**
 * @brief     interface timestamp read
 * @param[in] *t points to an ir_remote_time structure
 * @return    status code
 *            - 0 success
 *            - 1 read failed
 * @note      none
 */
uint8_t ir_remote_interface_timestamp_read(ir_remote_time_t *t);

/**
 * @brief     interface delay ms
 * @param[in] ms
 * @note      none
 */
void ir_remote_interface_delay_ms(uint32_t ms);

/**
 * @brief     interface print format data
 * @param[in] fmt is the format data
 * @note      none
 */
void ir_remote_interface_debug_print(const char *const fmt, ...);

/**
 * @brief     interface receive callback
 * @param[in] *data points to an ir_remote_t structure
 * @note      none
 */
void ir_remote_interface_receive_callback(ir_remote_t *data);


uint8_t ir_remote_interface_timer_init(void);


#ifdef __cplusplus
}
#endif

#endif

driver_ir_remote_interface.h

#include "driver_ir_remote_interface.h"
#include "timestamp.h"
#include "delay.h"
#include "stdarg.h"

/**
 * @brief timer var definition
 */
static uint64_t gs_second = 0;

/**
 * @brief timer callback
 * @param us is the timer cnt
 */
static void gs_tim_irq(uint32_t us)
{   
    gs_second += us / 1000000;
}

/**
 * @brief interface timer init
 * @param  
 * @return 0 - success  1 - failed
 */
uint8_t ir_remote_interface_timer_init(void)
{
    /* timer init */
    if (tim_init(1000000, gs_tim_irq) != 0)
    {
        return 1;
    }
    
    /* timer start */
    if (tim_start() != 0)
    {
        return 1;
    }
    
    return 0;
} 

/**
 * @brief     interface timestamp read
 * @param[in] *t points to an ir_remote_time structure
 * @return    status code
 *            - 0 success
 *            - 1 read failed
 * @note      none
 */
uint8_t ir_remote_interface_timestamp_read(ir_remote_time_t *t)
{
    t->s = gs_second;
    t->us = tim_get_handle()->Instance->CNT;
    return 0;
}

/**
 * @brief     interface delay ms
 * @param[in] ms
 * @note      none
 */
void ir_remote_interface_delay_ms(uint32_t ms)
{
    delay_ms((uint16_t)ms);
}

/**
 * @brief     interface print format data
 * @param[in] fmt is the format data
 * @note      none
 */
void ir_remote_interface_debug_print(const char *const fmt, ...)
{
    // char str[256];
    // uint16_t len;
    // va_list args;
    
    // memset((char *)str, 0, sizeof(char) * 256); 
    // va_start(args, fmt);
    // vsnprintf((char *)str, 255, (char const *)fmt, args);
    // va_end(args);
    
    // len = strlen((char *)str);
    // extern UART_HandleTypeDef huart1;
    // HAL_UART_Transmit_IT(&huart1, (uint8_t *)str, len);
    printf(fmt);
    // fflush(stdout);
}

/**
 * @brief     interface receive callback
 * @param[in] *data points to an ir_remote_t structure
 * @note      none
 */
void ir_remote_interface_receive_callback(ir_remote_t *data)
{
    switch (data->status)
    {
        case IR_REMOTE_STATUS_OK :
        {
            ir_remote_interface_debug_print("ir_remote: irq ok.\n");
            // ir_remote_interface_debug_print("ir_remote: add is 0x%02X and cmd is 0x%02X.\n", data->address, data->command);
            uint8_t add = data->address;
            uint8_t cmd = data->command;
            ir_remote_interface_debug_print("ir_remote: add is 0x%02x and cmd is 0x%02x.\r\n", add, cmd);

            break;
        }
        case IR_REMOTE_STATUS_REPEAT :
        {
            ir_remote_interface_debug_print("ir_remote: irq repeat.\n");
            ir_remote_interface_debug_print("ir_remote: add is 0x%02X and cmd is 0x%02X.\n", data->address, data->command);
            
            break;
        }
        case IR_REMOTE_STATUS_ADDR_ERR :
        {
            ir_remote_interface_debug_print("ir_remote: irq addr error.\n");
            
            break;
        }
        case IR_REMOTE_STATUS_CMD_ERR :
        {
            ir_remote_interface_debug_print("ir_remote: irq cmd error.\n");
            
            break;
        }
        case IR_REMOTE_STATUS_FRAME_INVALID :
        {
            ir_remote_interface_debug_print("ir_remote: irq frame invalid.\n");
            
            break;
        }
        default :
        {
            ir_remote_interface_debug_print("ir_remote: irq unknown status.\n");
            
            break;
        }
    }
}

driver_ir_remote_test.h

#ifndef __DRIVER_IR_REMOTE_RECEIVE_TEST_H__
#define __DRIVER_IR_REMOTE_RECEIVE_TEST_H__

#include "driver_ir_remote_interface.h"
#include "driver_ir_remote_send.h"

#ifdef __cplusplus
extern "C"{
#endif

/**
 * @defgroup ir_remote_test_driver ir_remote test driver function
 * @brief    ir_remote test driver modules
 * @ingroup  ir_remote_driver
 * @{
 */

/**
 * @brief  receive test irq
 * @return status code
 *         - 0 success
 *         - 1 run failed
 * @note   none
 */
uint8_t ir_remote_receive_test_irq_handler(void);

/**
 * @brief     receive test
 * @param[in] times is the test times
 * @return    status code
 *            - 0 success
 *            - 1 test failed
 * @note      none
 */
uint8_t ir_remote_receive_test(uint32_t times);

/**
 * @}
 */

#ifdef __cplusplus
}
#endif

#endif

driver_ir_remote_test.c

#include "driver_ir_remote_test.h"

static ir_remote_handle_t gs_handle;        /**< ir_remote handle */
static volatile uint8_t gs_flag;            /**< flag */

/**
 * @brief  receive test irq
 * @return status code
 *         - 0 success
 *         - 1 run failed
 * @note   none
 */
uint8_t ir_remote_receive_test_irq_handler(void)
{
    // ir_remote_irq_handler(&gs_handle);
    if (ir_remote_irq_handler(&gs_handle) != 0)
    {
        return 1;
    }
    else
    {
        return 0;
    }
}

/**
 * @brief     interface receive callback
 * @param[in] *data points to an ir_remote_t structure
 * @note      none
 */
static void a_receive_callback(ir_remote_t *data)
{
    switch (data->status)
    {
        case IR_REMOTE_STATUS_OK :
        {
            ir_remote_interface_debug_print("ir_remote: irq ok.\r\n");
            // ir_remote_interface_debug_print("ir_remote: add is 0x%02X and cmd is 0x%02X.\r\n", data->address, data->command);
            uint8_t add = data->address;
            uint8_t cmd = data->command;
            ir_remote_interface_debug_print("ir_remote: add is 0x%02x and cmd is 0x%02x.\r\n", add, cmd);
            switch (cmd)
            {
            case 0x07:
                ir_remote_interface_debug_print("|<< |<< |<<\r\n");
                break;
            case 0x08:
                ir_remote_interface_debug_print("444444\r\n");
                break;
            case 0x09:
                ir_remote_interface_debug_print(">>|  >>|  >>|\r\n");
                break; 
            case 0x0c:
                ir_remote_interface_debug_print("111111\r\n");
                break;    
            case 0x0d:
                ir_remote_interface_debug_print("cccccc\r\n");
                break;
            case 0x15:
                ir_remote_interface_debug_print(">>>>>>\r\n");
                break;
            case 0x16:
                ir_remote_interface_debug_print("000000\r\n");
                break;
            case 0x18:
                ir_remote_interface_debug_print("222222\r\n");
                break;
            case 0x19:
                ir_remote_interface_debug_print("-------\r\n");
                break;
            case 0x1c:
                ir_remote_interface_debug_print("555555\r\n");
                break;
            case 0x40:
                ir_remote_interface_debug_print("+++++++++++++++++\r\n");
                break;
            case 0x42:
                ir_remote_interface_debug_print("7777777777777777777\r\n");
                break;
            case 0x43:
                ir_remote_interface_debug_print("back  back  back\r\n");
                break;
            case 0x45:
                ir_remote_interface_debug_print("power power power\r\n");
                break;
            case 0x44:
                ir_remote_interface_debug_print("test test test\r\n");
                break;
            case 0x47:
                ir_remote_interface_debug_print("menu  menu  menu\r\n");
                break;
            case 0x4a:
                ir_remote_interface_debug_print("999999999999999999\r\n");
                break;
            case 0x5e:
                ir_remote_interface_debug_print("33333333333333333\r\n");
                break;
            case 0x5a:
                ir_remote_interface_debug_print("66666666666666\r\n");
                break;
            case 0x52:
                ir_remote_interface_debug_print("8888888888888888\r\n");
                break;
            default:
                break;
            }

            gs_flag = 1;
            
            break;
        }
        case IR_REMOTE_STATUS_REPEAT :
        {
            ir_remote_interface_debug_print("ir_remote: irq repeat.\r\n");
            ir_remote_interface_debug_print("ir_remote: add is 0x%02X and cmd is 0x%02X.\r\n", data->address, data->command);
            
            break;
        }
        case IR_REMOTE_STATUS_ADDR_ERR :
        {
            ir_remote_interface_debug_print("ir_remote: irq addr error.\r\n");
            
            break;
        }
        case IR_REMOTE_STATUS_CMD_ERR :
        {
            ir_remote_interface_debug_print("ir_remote: irq cmd error.\r\n");
            
            break;
        }
        case IR_REMOTE_STATUS_FRAME_INVALID :
        {
            ir_remote_interface_debug_print("ir_remote: irq frame invalid.\r\n");
            
            break;
        }
        default :
        {
            ir_remote_interface_debug_print("ir_remote: irq unknown status.\r\n");
            
            break;
        }
    }

    if (data->status == IR_REMOTE_STATUS_OK)
    {   
        extern TIM_HandleTypeDef htim3;
        ir_remote_send_init(&htim3, TIM_CHANNEL_2);
        ir_remote_send_frame_data(data);
        ir_remote_interface_debug_print("ir_remote: send ok.\r\n");
    }
    
}

/**
 * @brief     receive test
 * @param[in] times is the test times
 * @return    status code
 *            - 0 success
 *            - 1 test failed
 * @note      none
 */
uint8_t ir_remote_receive_test(uint32_t times)
{
    uint8_t res;
    uint16_t timeout;
    uint32_t i;
    ir_remote_info_t info;
    
    /* link interface function */
    DRIVER_IR_REMOTE_LINK_INIT(&gs_handle, ir_remote_handle_t);
    DRIVER_IR_REMOTE_LINK_TIMESTAMP_READ(&gs_handle, ir_remote_interface_timestamp_read);
    DRIVER_IR_REMOTE_LINK_DELAY_MS(&gs_handle, ir_remote_interface_delay_ms);
    DRIVER_IR_REMOTE_LINK_DEBUG_PRINT(&gs_handle, ir_remote_interface_debug_print);
    DRIVER_IR_REMOTE_LINK_RECEIVE_CALLBACK(&gs_handle, a_receive_callback);
    
    /* get information */
    res = ir_remote_info(&info);
    if (res != 0)
    {
        ir_remote_interface_debug_print("ir_remote: get info failed.\r\n");
       
        return 1;
    }
    else
    {
        /* print chip info */
        ir_remote_interface_debug_print("ir_remote: chip is %s.\r\n", info.chip_name);
        // ir_remote_interface_debug_print("ir_remote: manufacturer is %s.\r\n", info.manufacturer_name);
        // ir_remote_interface_debug_print("ir_remote: interface is %s.\r\n", info.interface);
        // ir_remote_interface_debug_print("ir_remote: driver version is %d.%d.\r\n", info.driver_version / 1000, (info.driver_version % 1000) / 100);
        // ir_remote_interface_debug_print("ir_remote: min supply voltage is %0.1fV.\r\n", info.supply_voltage_min_v);
        // ir_remote_interface_debug_print("ir_remote: max supply voltage is %0.1fV.\r\n", info.supply_voltage_max_v);
        // ir_remote_interface_debug_print("ir_remote: max current is %0.2fmA.\r\n", info.max_current_ma);
        // ir_remote_interface_debug_print("ir_remote: max temperature is %0.1fC.\r\n", info.temperature_max);
        // ir_remote_interface_debug_print("ir_remote: min temperature is %0.1fC.\r\n", info.temperature_min);
    }
    
    /* init */
    res = ir_remote_init(&gs_handle);
    if (res != 0)
    {
        ir_remote_interface_debug_print("ir_remote: init failed.\r\n");
       
        return 1;
    }
    
    /* start receive test */
    ir_remote_interface_debug_print("ir_remote: start receive test.\r\n");
    
    /* loop */
    for (i = 0; i < times; i++)
    {
        /* 5s timeout */
        timeout = 500;
        
        /* init 0 */
        gs_flag = 0;
        
        /* check timeout */
        while (timeout != 0)
        {
            /* check the flag */
            /* 判断红外接收器的引脚是否产生中断 */
            if (gs_flag != 0)
            {
                break;
            }
            
            /* timeout -- */
            timeout--;
            
            /* delay 10ms */
            ir_remote_interface_delay_ms(10);
        }
        
        /* check the timeout */
        /* 判断是由于超时结束的循环,还是由于中断结束的循环 */
        if (timeout == 0)
        {
            /* receive timeout */
            ir_remote_interface_debug_print("ir_remote: receive timeout.\r\n");
            (void)ir_remote_deinit(&gs_handle);
                
            return 1;
        }
    }
    
    /* finish receive test */
    ir_remote_interface_debug_print("ir_remote: finish receive test.\r\n");
    (void)ir_remote_deinit(&gs_handle);
    
    return 0;
}