一.任务需求

1. 采用stm32F103和HC-SR04超声波模块, 使用标准库或HAL库+ 定时器中断,完成1或2路的超声波障碍物测距功能。

2. 当前智能汽车上一般配置有12路超声波雷达,这些专用超声波雷达内置了MCU,直接输出数字化的测距结果,一般硬件接口采用串口RS485,通信协议采用modbus。请思考:

  • RS485与RS232(UART)有什么不同?

  • Modbus协议是什么?

  • 如果让你设计一款 12路车载超声波雷达,采用 stm32F103+HC-SR04超声波模块,对外提供RS485和Modbus协议,你的设计方案是什么?

  • 二.超声波测距实验过程

    1.创建项目

    设置sys

    设置rcc

    定时器tim2设置

    定时器tim3设置

    gpio管脚设置:

    之后导入RTthread,导入过程参考:https://blog.csdn.net/lxr0106/article/details/134635908

    2.编写代码

    SC04.h:

    #ifndef __SR04_H
    #define __SR04_H
    #include "main.h"
    #include "tim.h"
    #include "stdio.h"
    #include "rtthread.h"
    #define TRIG_H  HAL_GPIO_WritePin(Trig_GPIO_Port,Trig_Pin,GPIO_PIN_SET)
    #define TRIG_L  HAL_GPIO_WritePin(Trig_GPIO_Port,Trig_Pin,GPIO_PIN_RESET)
    extern float distant;
    void delay_us(uint32_t us);
    void SR04_GetData(void);
    void rt_hw_us_delay(rt_uint32_t us);
    #endif
    

    SC04.c:

    
    #include "SR04.h"
    
    float distant;      //测量距离
    uint32_t measure_Buf[3] = {0};   //存放定时器计数值的数组
    uint8_t  measure_Cnt = 0;    //状态标志位
    uint32_t high_time;   //超声波模块返回的高电平时间
    
    
    //===============================================读取距离
    void SR04_GetData(void)
    {
    switch (measure_Cnt){
    	case 0:
             TRIG_H;
             rt_hw_us_delay(30);
             TRIG_L;
        
    		measure_Cnt++;
    		__HAL_TIM_SET_CAPTUREPOLARITY(&htim2, TIM_CHANNEL_1, TIM_INPUTCHANNELPOLARITY_RISING);
    		HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_1);	//启动输入捕获       或者: __HAL_TIM_ENABLE(&htim5);                                                                                    		
            break;
    	case 3:
    		high_time = measure_Buf[1]- measure_Buf[0];    //高电平时间
             printf("\r\n----高电平时间-%d-us----\r\n",high_time);							
    		distant=(high_time*0.034)/2;  //单位cm
            printf("\r\n-检测距离为-%.2f-cm-\r\n",distant);          
    		measure_Cnt = 0;  //清空标志位
            TIM2->CNT=0;     //清空计时器计数
    		break;
    				
    	}
    }
    
    
    //===============================================us延时函数
        void delay_us(uint32_t us)//主频72M
    {
        uint32_t delay = (HAL_RCC_GetHCLKFreq() / 4000000 * us);
        while (delay--)
    	{
    		;
    	}
    }
    void rt_hw_us_delay(rt_uint32_t us)
    {
        rt_uint32_t ticks;
        rt_uint32_t told, tnow, tcnt = 0;
        rt_uint32_t reload = SysTick->LOAD;
    
        /* 获得延时经过的 tick 数 */
        ticks = us * reload / (1000000 / RT_TICK_PER_SECOND);
        /* 获得当前时间 */
        told = SysTick->VAL;
        while (1)
        {
            /* 循环获得当前时间,直到达到指定的时间后退出循环 */
            tnow = SysTick->VAL;
            if (tnow != told)
            {
                if (tnow < told)
                {
                    tcnt += told - tnow;
                }
                else
                {
                    tcnt += reload - tnow + told;
                }
                told = tnow;
                if (tcnt >= ticks)
                {
                    break;
                }
            }
        }
    }
    
    
    //===============================================中断回调函数
    void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)//
    {
    	
    	if(TIM2 == htim->Instance)// 判断触发的中断的定时器为TIM2
    	{
    		switch(measure_Cnt){
    			case 1:
    				measure_Buf[0] = HAL_TIM_ReadCapturedValue(&htim2,TIM_CHANNEL_1);//获取当前的捕获值.
    				__HAL_TIM_SET_CAPTUREPOLARITY(&htim2,TIM_CHANNEL_1,TIM_ICPOLARITY_FALLING);  //设置为下降沿捕获
    				measure_Cnt++;                                            
    				break;              
    			case 2:
    				measure_Buf[1] = HAL_TIM_ReadCapturedValue(&htim2,TIM_CHANNEL_1);//获取当前的捕获值.
    				HAL_TIM_IC_Stop_IT(&htim2,TIM_CHANNEL_1); //停止捕获   或者: __HAL_TIM_DISABLE(&htim5);
    				measure_Cnt++;  
                             
    		}
    	
    	}
    	
    }
    
    
    
    
    

    main.c:

    /* USER CODE BEGIN Header */
    /**
      ******************************************************************************
      * @file           : main.c
      * @brief          : Main program body
      ******************************************************************************
      * @attention
      *
      * <h2><center>&copy; Copyright (c) 2022 STMicroelectronics.
      * All rights reserved.</center></h2>
      *
      * This software component is licensed by ST under BSD 3-Clause license,
      * the "License"; You may not use this file except in compliance with the
      * License. You may obtain a copy of the License at:
      *                        opensource.org/licenses/BSD-3-Clause
      *
      ******************************************************************************
      */
    /* USER CODE END Header */
    /* Includes ------------------------------------------------------------------*/
    #include "main.h"
    #include "tim.h"
    #include "usart.h"
    #include "gpio.h"
    
    /* Private includes ----------------------------------------------------------*/
    /* USER CODE BEGIN Includes */
    #include "SR04.h"
    #include <rtthread.h>
    
    /* USER CODE END Includes */
    
    /* Private typedef -----------------------------------------------------------*/
    /* USER CODE BEGIN PTD */
    
    /* USER CODE END PTD */
    
    /* Private define ------------------------------------------------------------*/
    /* USER CODE BEGIN PD */
    /* USER CODE END PD */
    
    /* Private macro -------------------------------------------------------------*/
    /* USER CODE BEGIN PM */
    
    /* USER CODE END PM */
    
    /* Private variables ---------------------------------------------------------*/
    
    /* USER CODE BEGIN PV */
    
    /* USER CODE END PV */
    
    /* Private function prototypes -----------------------------------------------*/
    void SystemClock_Config(void);
    /* USER CODE BEGIN PFP */
    
    /* USER CODE END PFP */
    
    /* Private user code ---------------------------------------------------------*/
    /* USER CODE BEGIN 0 */
    
    /* USER CODE END 0 */
    
    /**
      * @brief  The application entry point.
      * @retval int
      */
    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_TIM2_Init();
      MX_USART1_UART_Init();
      MX_TIM3_Init();
      /* USER CODE BEGIN 2 */
    HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_1);
        /* 创建线程 */
        rt_thread_t beep_control_task = rt_thread_create("beep_control",/* 线程名称 */
                                beep_control, RT_NULL,
                                1024, 3, 10); //
        if(beep_control_task != RT_NULL)
        {
            /* 启动线程 */
            rt_thread_startup(beep_control_task);
            rt_kprintf("beep_control_task is already started\n");
            
        }
        else
        {
            rt_kprintf("beep_control_task thread is not started\n");
        }
    
        rt_thread_t led_control_task = rt_thread_create("led_control",/* 线程名称 */
                                led_control, RT_NULL,
                                1024, 3, 10); //
        if(led_control_task != RT_NULL)
        {
            /* 启动线程 */
            rt_thread_startup(led_control_task);
            rt_kprintf("led_control_task is already started\n");
            
        }
        else
        {
            rt_kprintf("led_control_task thread is not started\n");
        }
      /* USER CODE END 2 */
    
      /* Infinite loop */
      /* USER CODE BEGIN WHILE */
           printf("getting...\n");
      while (1)
      {
    
          SR04_GetData();
          rt_thread_mdelay(1000);
    
        /* USER CODE END WHILE */
    
        /* USER CODE BEGIN 3 */
      }
      /* USER CODE END 3 */
    }
    
    /**
      * @brief System Clock Configuration
      * @retval None
      */
    void SystemClock_Config(void)
    {
      RCC_OscInitTypeDef RCC_OscInitStruct = {0};
      RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
    
      /** Initializes the RCC Oscillators according to the specified parameters
      * in the RCC_OscInitTypeDef structure.
      */
      RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
      RCC_OscInitStruct.HSEState = RCC_HSE_ON;
      RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
      RCC_OscInitStruct.HSIState = RCC_HSI_ON;
      RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
      RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
      RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
      if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
      {
        Error_Handler();
      }
    
      /** Initializes the CPU, AHB and APB buses clocks
      */
      RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                                  |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
      RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
      RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
      RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
      RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
    
      if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
      {
        Error_Handler();
      }
    }
    
    /* USER CODE BEGIN 4 */
    void beep_control(void* promt){
      while (1)
      {
        int distant_int = distant;
        HAL_GPIO_WritePin(beep_GPIO_Port,beep_Pin,GPIO_PIN_RESET);
        rt_thread_mdelay(distant_int * 100);
        HAL_GPIO_WritePin(beep_GPIO_Port,beep_Pin,GPIO_PIN_SET);
        rt_thread_mdelay(distant_int * 100);
      }
    }
    void led_control(void *promt)
    {
        while (1)
      {
        float pwm_state = (20.0-distant)/20.0;
    		int distant_int = distant;
        int distant_pwm = (int)(pwm_state*500);
        __HAL_TIM_SetCompare(&htim3, TIM_CHANNEL_1,distant_pwm);
        rt_thread_mdelay(1000);
      }
    }
    /* USER CODE END 4 */
    
    /**
      * @brief  This function is executed in case of error occurrence.
      * @retval None
      */
    void Error_Handler(void)
    {
      /* USER CODE BEGIN Error_Handler_Debug */
      /* User can add his own implementation to report the HAL error return state */
      __disable_irq();
      while (1)
      {
      }
      /* USER CODE END Error_Handler_Debug */
    }
    
    #ifdef  USE_FULL_ASSERT
    /**
      * @brief  Reports the name of the source file and the source line number
      *         where the assert_param error has occurred.
      * @param  file: pointer to the source file name
      * @param  line: assert_param error line source number
      * @retval None
      */
    void assert_failed(uint8_t *file, uint32_t line)
    {
      /* USER CODE BEGIN 6 */
      /* User can add his own implementation to report the file name and line number,
         ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
      /* USER CODE END 6 */
    }
    #endif /* USE_FULL_ASSERT */
    
    
    
    

    3.实验效果


    三. Modbus协议介绍

    1. Modbus协议简介

    Modbus 是一种应用层协议,主要用于工业自动化领域。它支持多种通信方式,包括RS232、RS485等。Modbus 协议定义了设备之间的通信方式,包括数据格式、错误检测和设备地址等。它允许设备通过Modbus协议进行读写操作,实现数据的交换。

    2.RS485与RS232(UART)的不同:

  • RS485 是一种差分信号通信协议,具有更强的抗干扰能力,适合长距离通信。它支持多点通信,即一个总线上可以连接多个设备,并且能够通过地址区分不同的设备。
  • RS232(也称为UART)是一种单端信号通信协议,通常用于短距离通信,不支持多点通信。它通常用于点对点的通信方式。
  • 3. 设计方案:

  • 硬件设计:
  • 使用 STM32F103 微控制器作为主控单元,因为它具有足够的处理能力和丰富的外设接口。
  • 每个 HC-SR04 超声波模块连接到一个GPIO引脚,用于触发和接收超声波的回波信号。
  • 使用RS485通信接口芯片,如 MAX485SP485,连接到微控制器的相应引脚,实现数据的串行通信。
  • 软件设计:
  • 在STM32F103上编写固件,实现对HC-SR04模块的控制,包括触发超声波发射和读取回波时间。
  • 实现Modbus协议栈,处理Modbus RTU(二进制模式)或Modbus TCP(网络模式)的数据包。
  • 设计设备地址和功能码,以便在Modbus网络上识别和控制各个超声波雷达。
  • 实现错误检测和处理机制,确保数据的准确性和通信的可靠性。
  • 通信协议:
  • 设计通信协议,定义如何通过RS485发送和接收数据,包括数据帧的格式、起始位、数据位、校验位和停止位。
  • 确保Modbus协议的实现能够处理不同类型的功能码,如读取保持寄存器、写入保持寄存器等。
  • 系统整合:
  • 将硬件和软件整合,进行系统级的测试,确保超声波雷达能够准确测量距离,并通过Modbus协议正确地与外部系统通信。
  • 四.参考文件

    https://blog.csdn.net/lxr0106/article/details/139260191

    作者:舲639

    物联沃分享整理
    物联沃-IOTWORD物联网 » Stm32超声波测距实验

    发表回复