STM32
直播中

goodmbby

8年用户 1155经验值
擅长:处理器/DSP
私信 关注
[问答]

如何利用STM32定时器输入捕获实现超声波测距?

STM32实现定时器输入捕获原理是什么?
如何利用STM32定时器输入捕获实现超声波测距?

回帖(1)

h1654155275.5879

2021-11-18 10:47:17

  
    0、前言

    有关STM32实现定时器输入捕获原理戳这里
    1、硬件设计

  1.1、超声波模块

    硬件设计采用CS102驱动的收发一体超声波探头作为传感器,产品型号为US-116,可实现 25.5cm-350cm 的非接触测距功能,通过计算可知输出脉冲宽度大约为1.5~206ms。

  

  

    ▲ 超声波板原理图   
     以下文字及图片来源于CS102数据手册。
  
    工作原理:在 TRIG 管脚输入一个 10US 以上的高电平(一般建议 50US~100US),芯片(TP,TN管脚)便可发出 8 个 40KHZ的超声波脉冲,然后(RP,RN)检测回波信号。当检测到回波信号后,通过 ECHO 管脚输出,如下图所示:

  

  

    ▲ 测距时序图     根据 ECHO 管脚输出高电平的持续时间可以计算距离值。即距离值为:(高电平时间*340m/s)/2。
  当测量距离超过测量范围时,CS102仍会通过 ECHO 管脚输出高电平的信号,高电平的宽度约为 33ms。如下图所示:

  

  

    ▲ 超出测量范围时序图     测量周期:当芯片通过ECHO 管脚输出的高电平脉冲后,便可进行下一次测量,所以测量周期取决于测量距离,当测距很近时,ECHO返回的脉冲宽度较窄,测量周期就很短;当测距较远时,ECHO返回的脉冲宽度较宽,测量周期也就相应的变长。
  最坏情况下,被测物体超出测量范围,此时返回的脉冲宽度最长,约为 33ms,所以最坏情况下的测量周期稍大于 33ms 即可(取 40ms足够)。

  

  

    ▲ US116主要技术参数     上图指出了本次实验使用的超声波模块的技术参数,值得注意的是探测距离为25.5-350cm,也就是说在该范围之外的超声波数据可以认为错误数据而舍去。
  1.2、STM32L151

    将PA6即TIM3_CH1用于输入捕获。PA7用以发送超声波触发信号。

  

  

    ▲ STM32L151官方文档截图   2、CubeMX配置

  
  

  

    ▲ CubeMX 输入捕获TIM配置图   为什么取32-1分频?
  根据数据手册显示的数据来计算,高电平最高持续时间按40ms来计算则65536/(40/1000)=1,638,400Hz也就是说定时器的时钟频率要小于这个频率,留下一部分冗余取整可取1MHz,最小计时精度为1us,若按照标准声速340m/s计算测量误差在±0.34mm在接受范围内。

  

  

    ▲ CubeMX 打开TIM3全局中断   
  

  

    ▲ CubeMX 设置输入捕获GPIO下拉     有一点疑问的是在CubeMX的默认设置中GPIO会被设置成复用推挽输出而手动将其更改为输入模式后将会导致捕获功能不可用。

  

  

    ▲ CubeMX 配置超声波触发信号发送引脚  

3、软件设计
实现us级延时


//实现简单的us级延时
void RCCdelay_us(uint32_t udelay)
{
  __IO uint32_t Delay = udelay * 32 / 8;//(SystemCoreClock / 8U / 1000000U)
  do
  {
    __NOP();
  }
  while (Delay --);
}


main.c


...
int main(void)
{
  /* USER CODE BEGIN 1 */


  /* USER CODE END 1 */


  /* MCU Configuration--------------------------------------------------------*/


  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();


  /* USER CODE BEGIN Init */


  /* USER CODE END Init */


  /* Configure the system clock */
  SystemClock_Config();


  /* USER CODE BEGIN SysInit */


  /* USER CODE END SysInit */


  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  MX_TIM3_Init();
  /* USER CODE BEGIN 2 */
       
  /* USER CODE END 2 */


  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
                if(CapValReady == 1)
                {
                        //25.5cm-350cm
                        USData = CapVal * 34000/1000000/2;//声速34000cm/s TIM3Freq:1000000Hz
                        if(USData <= 350 && USData >= 25.5)
                        {
                                printf("US:%.2fcmrn",USData);
                        }
                        __HAL_TIM_DISABLE(&htim3);
                        HAL_Delay(500);
                        __HAL_TIM_SET_COUNTER(&htim3,0);//设置定时器3计数值为0
                        __HAL_TIM_ENABLE(&htim3);
                        CapValReady = 0;
                        USTrig();
                        HAL_TIM_IC_Start_IT(&htim3,TIM_CHANNEL_1);//定时器3通道1输入捕获使能
                }
    /* USER CODE END WHILE */


    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}


tim.c


/* USER CODE BEGIN 0 */
uint8_t CapIndex = 0;
uint8_t CapValReady = 1;
float USData = 0;
uint16_t CapVal = 0;


/* USER CODE END 0 */
...
/* USER CODE BEGIN 1 */


void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
        if(htim->Instance == TIM3)
        {
                if(CapIndex == 0)
                {
                        CapIndex = 1;
                        __HAL_TIM_SET_COUNTER(htim,0);//设置定时器3计数值为0
                        //清除原来的捕获极性
                        TIM_RESET_CAPTUREPOLARITY(htim,TIM_CHANNEL_1);   
                        //定时器2通道1设置为下降沿捕获
                        TIM_SET_CAPTUREPOLARITY(htim,TIM_CHANNEL_1,TIM_ICPOLARITY_FALLING);
                       
                }
                else
                {
                        HAL_TIM_IC_Stop_IT(htim,TIM_CHANNEL_1);//关闭TIM2_CH1输入捕获
                        CapVal = __HAL_TIM_GET_COMPARE(htim,TIM_CHANNEL_1);
                        CapValReady = 1;
                        //清除原来的捕获极性
                        TIM_RESET_CAPTUREPOLARITY(htim,TIM_CHANNEL_1);   
                        //定时器2通道1设置为上升沿捕获
                        TIM_SET_CAPTUREPOLARITY(htim,TIM_CHANNEL_1,TIM_ICPOLARITY_RISING);
                        CapIndex = 0;
                }
               
        }
}
/* USER CODE END 1 */


gpio.c


...
/* USER CODE BEGIN 2 */
//实现简单的us级延时
void RCCdelay_us(uint32_t udelay)
{
  __IO uint32_t Delay = udelay * 32 / 8;//(SystemCoreClock / 8U / 1000000U)
  do
  {
    __NOP();
  }
  while (Delay --);
}
void USTrig()
{
        HAL_GPIO_WritePin(Trig_GPIO_Port,Trig_Pin,GPIO_PIN_SET);
        RCCdelay_us(50);
        HAL_GPIO_WritePin(Trig_GPIO_Port,Trig_Pin,GPIO_PIN_RESET);
}
/* USER CODE END 2 */




函数及宏定义介绍:
__HAL_TIM_SET_COUNTER
说明: 设置运行时的TIM计数器寄存器值。
参数:
  __HANDLE__:TIM handle.
  __COUNTER__: 指定计数器寄存器新值。
  __HAL_TIM_GET_COMPARE
说明: 获取运行时的TIM捕获比较寄存器值。
参数:
  __HANDLE__:TIM handle.
  __CHANNEL__: 与捕获比较寄存器相关联的TIM通道此参数可以是以下值之一:
      TIM_CHANNEL_1:获取捕获/比较1寄存器值
      TIM_CHANNEL_2:获取捕获/比较2寄存器值
      TIM_CHANNEL_3:获取捕获/比较3寄存器值
      TIM_CHANNEL_4:获取捕获/比较4寄存器值
  HAL_StatusTypeDef HAL_TIM_IC_Start_IT (TIM_HandleTypeDef * htim, uint32_t Channel)
说明: 在中断模式下启动TIM输入捕获测量。
参数:
   htim: 指向包含TIM模块配置信息的TIM_HandleTypeDef结构的指针。
   Channel: 将启用TIM通道。 此参数可以是以下值之一:
      TIM_CHANNEL_1:选择TIM通道1
      TIM_CHANNEL_2:选择TIM通道2
      TIM_CHANNEL_3:选择TIM通道3
      TIM_CHANNEL_4:选择TIM通道4
返回值:HAL status
  HAL_StatusTypeDef HAL_TIM_IC_Stop_IT (TIM_HandleTypeDef * htim, uint32_t Channel)
说明: 在中断模式下停止TIM输入捕获测量。
参数:
   htim: 指向包含TIM模块配置信息的TIM_HandleTypeDef结构的指针。
   Channel: 将启用TIM通道。 此参数可以是以下值之一:
      TIM_CHANNEL_1:选择TIM通道1
      TIM_CHANNEL_2:选择TIM通道2
      TIM_CHANNEL_3:选择TIM通道3
      TIM_CHANNEL_4:选择TIM通道4
返回值:HAL status

  

  

    ▲ 实现效果图
举报

更多回帖

发帖
×
20
完善资料,
赚取积分