STM32视频教程

红外接收代码实现

作者:陈广
日期:2021-12-22


视频播放地址

点击播放

tim.c代码(寄存器版):

#include "tim.h"

void MX_TIM3_Init(void)
{
  LL_TIM_InitTypeDef TIM_InitStruct = {0};
  LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
  /* Peripheral clock enable */
  LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM3);
  LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_GPIOA);
  /**TIM3 GPIO Configuration
  PA7   ------> TIM3_CH2
  */
  GPIO_InitStruct.Pin = LL_GPIO_PIN_7;
  GPIO_InitStruct.Mode = LL_GPIO_MODE_FLOATING;
  LL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /* TIM3 interrupt Init */
  NVIC_SetPriority(TIM3_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));
  NVIC_EnableIRQ(TIM3_IRQn);

  TIM_InitStruct.Prescaler = 71;
  TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP;
  TIM_InitStruct.Autoreload = 65535;
  TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1;
  LL_TIM_Init(TIM3, &TIM_InitStruct);
  LL_TIM_DisableARRPreload(TIM3);
  LL_TIM_SetClockSource(TIM3, LL_TIM_CLOCKSOURCE_INTERNAL);
  LL_TIM_SetTriggerOutput(TIM3, LL_TIM_TRGO_RESET);
  LL_TIM_DisableMasterSlaveMode(TIM3);
  LL_TIM_IC_SetActiveInput(TIM3, LL_TIM_CHANNEL_CH2, LL_TIM_ACTIVEINPUT_DIRECTTI);
  LL_TIM_IC_SetPrescaler(TIM3, LL_TIM_CHANNEL_CH2, LL_TIM_ICPSC_DIV1);
  LL_TIM_IC_SetFilter(TIM3, LL_TIM_CHANNEL_CH2, LL_TIM_IC_FILTER_FDIV1);
  LL_TIM_IC_SetPolarity(TIM3, LL_TIM_CHANNEL_CH2, LL_TIM_IC_POLARITY_FALLING);
  /* USER CODE BEGIN TIM3_Init 2 */
  LL_TIM_CC_EnableChannel(TIM3, LL_TIM_CHANNEL_CH2); //启用通道2
  LL_TIM_EnableCounter(TIM3); //使能定时器计数器
  /* USER CODE END TIM3_Init 2 */
}

/* USER CODE BEGIN 1 */
volatile int cnt, end;
volatile uint8_t data[4] = {0};

/* 描述:等待红外信息并返回解析结果
 * 参数:
 *     addr:存放地址码的uint8_t指针
 * 返回:
 *     >=0:命令码
 *     -1:重复码
 *     -2:前导码的高电平错误
 *     -3:前导码低电平错误
 *     -4:数字码高电平错误
 *     -5:数字码低电平错误
 *     -6:等待超时
 *     -7:地址码校验错误
 *     -8:命令码校验错误
 */
int Wait_For_IR(uint8_t *addr)
{
    cnt = -2;
    end = 0;
    data[0] = 0;
    data[1] = 0;
    data[2] = 0;
    data[3] = 0;
    TIM3->CCER |= 0x20; //设置为下降沿触发
    TIM3->SR = 0; //清除中断标志位
    TIM3->DIER = 5; //打开更新和CC2中断
    while(!end);
    TIM3->DIER = 0; //关闭更新和CC2中断
    if(end < 0) return end;
    if(data[0] != (uint8_t)(~data[1])) return -7;
    if(data[2] != (uint8_t)(~data[3])) return -8;
    *addr = data[0];
    return data[2];
}

void TIM3_IRQHandler(void)
{
    if(TIM3->SR & 1) //定时器到期中断
    {
        TIM3->SR &= ~1; //清除中断标志位
        if(cnt >= -1 && cnt <= 32)
        {
            end = -6; //接收超时
        }
    }
    if(TIM3->SR & 4) //通道2中断
    {
        if(TIM3->CCER & 0x20) //如果为下降沿
        {
            uint32_t falling = TIM3->CCR2;
            TIM3->CNT = 0; //清空计数器
            TIM3->CCER &= ~0x20; //转换为上升沿触发
            if(cnt == -1) //前导码
            {   //重复码高电平为2.25ms
                if(falling > 2000 && falling < 3000)
                {
                    end = -1;
                    return;
                }
                //前导码高电平错误,正确为4.5ms
                if(falling < 3500 || falling > 5000) end = -2;
            }
            else if(cnt >= 0)
            {   //数字码高电平错误
                if(falling < 500 || falling > 1800)
                {
                    end = -4;
                    return;
                }
                //将读取到的数据压入data数组
                if(falling > 1125)
                {
                    data[cnt / 8] |= 1 << (cnt % 8);
                }
            }
            cnt++;
        }
        else //如果为上升沿
        {
            uint32_t rising = TIM3->CCR2;
            TIM3->CNT = 0; //清空计数器
            TIM3->CCER |= 0x20; //转换为下降沿触发
            if(cnt == -1) //前导码
            {   //前导码低电平错误,正确为9ms
                if(rising < 8000 || rising > 10000) end = -3;
            }
            else
            {   //数字码低电平错误,正确为560us
                if(rising < 500 || rising > 700)
                {
                    end = -5;
                    return;
                }
                if(cnt == 32) end = 1; //命令码读取结束
            }
        }
    }
}
/* USER CODE END 1 */

tim.c代码(LL库函数版)

#include "tim.h"
/* TIM3 init function */
void MX_TIM3_Init(void)
{

  /* USER CODE BEGIN TIM3_Init 0 */

  /* USER CODE END TIM3_Init 0 */

  LL_TIM_InitTypeDef TIM_InitStruct = {0};

  LL_GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* Peripheral clock enable */
  LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM3);

  LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_GPIOA);
  /**TIM3 GPIO Configuration
  PA7   ------> TIM3_CH2
  */
  GPIO_InitStruct.Pin = LL_GPIO_PIN_7;
  GPIO_InitStruct.Mode = LL_GPIO_MODE_FLOATING;
  LL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /* TIM3 interrupt Init */
  NVIC_SetPriority(TIM3_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));
  NVIC_EnableIRQ(TIM3_IRQn);

  /* USER CODE BEGIN TIM3_Init 1 */

  /* USER CODE END TIM3_Init 1 */
  TIM_InitStruct.Prescaler = 71;
  TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP;
  TIM_InitStruct.Autoreload = 65535;
  TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1;
  LL_TIM_Init(TIM3, &TIM_InitStruct);
  LL_TIM_DisableARRPreload(TIM3);
  LL_TIM_SetClockSource(TIM3, LL_TIM_CLOCKSOURCE_INTERNAL);
  LL_TIM_SetTriggerOutput(TIM3, LL_TIM_TRGO_RESET);
  LL_TIM_DisableMasterSlaveMode(TIM3);
  LL_TIM_IC_SetActiveInput(TIM3, LL_TIM_CHANNEL_CH2, LL_TIM_ACTIVEINPUT_DIRECTTI);
  LL_TIM_IC_SetPrescaler(TIM3, LL_TIM_CHANNEL_CH2, LL_TIM_ICPSC_DIV1);
  LL_TIM_IC_SetFilter(TIM3, LL_TIM_CHANNEL_CH2, LL_TIM_IC_FILTER_FDIV1);
  LL_TIM_IC_SetPolarity(TIM3, LL_TIM_CHANNEL_CH2, LL_TIM_IC_POLARITY_FALLING);
  /* USER CODE BEGIN TIM3_Init 2 */
  LL_TIM_CC_EnableChannel(TIM3, LL_TIM_CHANNEL_CH2); //启用通道2
  LL_TIM_EnableCounter(TIM3); //使能定时器计数器
  /* USER CODE END TIM3_Init 2 */
}

/* USER CODE BEGIN 1 */
volatile int cnt, end;
volatile uint8_t data[4] = {0};

/* 描述:等待红外信息并返回解析结果
 * 参数:
 *     addr:存放地址码的uint8_t指针
 * 返回:
 *     >=0:命令码
 *     -1:重复码
 *     -2:前导码的高电平错误
 *     -3:前导码低电平错误
 *     -4:数字码高电平错误
 *     -5:数字码低电平错误
 *     -6:等待超时
 *     -7:地址码校验错误
 *     -8:命令码校验错误
 */
int Wait_For_IR(uint8_t *addr)
{
    cnt = -2;
    end = 0;
    data[0] = 0;
    data[1] = 0;
    data[2] = 0;
    data[3] = 0;
    //设置为下降沿触发
    LL_TIM_IC_SetPolarity(TIM3, LL_TIM_CHANNEL_CH2, LL_TIM_IC_POLARITY_FALLING);
    LL_TIM_ClearFlag_UPDATE(TIM3); //清除计数器中断标志位
    LL_TIM_ClearFlag_CC2(TIM3); //清除通道2中断标志位
    LL_TIM_EnableIT_UPDATE(TIM3); //使能更新中断
    LL_TIM_EnableIT_CC2(TIM3); //使能通道2中断
    while(!end);
    LL_TIM_DisableIT_UPDATE(TIM3); //关闭更新中断
    LL_TIM_DisableIT_CC2(TIM3); //关闭通道2中断
    if(end < 0) return end;
    if(data[0] != (uint8_t)(~data[1])) return -7;
    if(data[2] != (uint8_t)(~data[3])) return -8;
    *addr = data[0];
    return data[2];
}

void TIM3_IRQHandler(void)
{
    if(LL_TIM_IsActiveFlag_UPDATE(TIM3)) //定时器到期中断
    {
        LL_TIM_ClearFlag_UPDATE(TIM3); //清除中断标志位
        if(cnt >= -1 && cnt <= 32)
        {
            end = -6; //接收超时
        }
    }
    if(LL_TIM_IsActiveFlag_CC2(TIM3)) //通道2中断
    {
        if(LL_TIM_IC_GetPolarity(TIM3, LL_TIM_CHANNEL_CH2) == LL_TIM_IC_POLARITY_FALLING) //如果为下降沿
        {
            uint32_t falling = LL_TIM_IC_GetCaptureCH2(TIM3);
            LL_TIM_SetCounter(TIM3, 0); //清空计数器
            LL_TIM_IC_SetPolarity(TIM3, LL_TIM_CHANNEL_CH2, LL_TIM_IC_POLARITY_RISING); //转换为上升沿触发
            if(cnt == -1) //前导码
            {   //重复码高电平为2.25ms
                if(falling > 2000 && falling < 3000)
                {
                    end = -1;
                    return;
                }
                //前导码高电平错误,正确为4.5ms
                if(falling < 3500 || falling > 5000) end = -2;
            }
            else if(cnt >= 0)
            {   //数字码高电平错误
                if(falling < 500 || falling > 1800)
                {
                    end = -4;
                    return;
                }
                //将读取到的数据压入data数组
                if(falling > 1125)
                {
                    data[cnt / 8] |= 1 << (cnt % 8);
                }
            }
            cnt++;
        }
        else //如果为上升沿
        {
            uint32_t rising = LL_TIM_IC_GetCaptureCH2(TIM3);
            LL_TIM_SetCounter(TIM3, 0); //清空计数器
            LL_TIM_IC_SetPolarity(TIM3, LL_TIM_CHANNEL_CH2, LL_TIM_IC_POLARITY_FALLING); //转换为下降沿触发
            if(cnt == -1) //前导码
            {   //前导码低电平错误,正确为9ms
                if(rising < 8000 || rising > 10000) end = -3;
            }
            else
            {   //数字码低电平错误,正确为560us
                if(rising < 500 || rising > 700)
                {
                    end = -5;
                    return;
                }
                if(cnt == 32) end = 1; //命令码读取结束
            }
        }
    }
}
/* USER CODE END 1 */
;

© 2018 - IOT小分队文章发布系统 v0.3