stm32项目平衡车详解(stm32F407)下

2023-11-02

stm32项目平衡车详解(stm32F407)下

本文章学习借鉴于创客学院团队,以表感谢。教学视频

B站学习地址

HC-SRO4 超声波测距避障功能开发
TSL1401 CCD摄像头实现小车巡线功能
小车通信功能分析及ESP32模块介绍
oled 显示置入屏幕显示小车状态



前言

前面我们已经实现了平衡小车的直立环,平衡环,转向环,下面我们将实现小车平衡小车蔽障与巡线功能的开发。如下如所示:
在这里插入图片描述
在这里插入图片描述


一、HC-SRO4 超声波测距避障功能开发

HC-SRO4超声波测距模块?

在这里插入图片描述
在这里插入图片描述
HC- SR04是一种超声波接近传感器,可以告诉您物体是否在它前面,并且还提供传感器和物体之间的距离。这些传感能力使其特别适用于需要知道远离物体或障碍物的机器人,例如墙壁或不应撞击的家具。

点击查看,HC-SR04超声波传感器原理

超声波测距避障功能开发

cube 搭建工程项目
在这里插入图片描述
下面给中断使能
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
启动定时器TIM7 去计时

在这里插入图片描述

在这里插入图片描述

避障模式开发

避障模式原理就是通过超声波加测距离,通过转向来躲避障碍物。所以我们会用转向环来控制小车转向。

//启动超声波,检测距离


//启动超声波检测函数

void HC_SRC04_Start(void)
{
	HAL_GPIO_WritePin(GPIOB,GPIO_PIN_0, GPIO_PIN_SET); //设置高电平启动
	
	delay_us(20); 
	
	HAL_GPIO_WritePin(GPIOB,GPIO_PIN_0, GPIO_PIN_RESET); //设置高低平启动
}

调用定时器中毒阿女函数进行计数,利用声音传播速度和接收时间测算距离


/**
  * @brief This function handles TIM7 global interrupt.
  */
void TIM7_IRQHandler(void)
{
  /* USER CODE BEGIN TIM7_IRQn 0 */

  /* USER CODE END TIM7_IRQn 0 */
  HAL_TIM_IRQHandler(&htim7);
  /* USER CODE BEGIN TIM7_IRQn 1 */

  /* USER CODE END TIM7_IRQn 1 */
}

/* USER CODE BEGIN 1 */
uint8_t ECHO_IRQ_FLAG = 0;

uint8_t time_cnt = 0; //参数二,溢出次数
static uint8_t last_distence =0;


void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	int time = 0;
	if(GPIO_Pin == GPIO_PIN_1)
	{
		if(ECHO_IRQ_FLAG == 0) //上升沿中断
		{
			ECHO_IRQ_FLAG=1; 
			HAL_TIM_Base_Start_IT(&htim7); //开启定时器,开始计时
		}else{
			ECHO_IRQ_FLAG = 0;   //下降沿中断
			HAL_TIM_Base_Stop_IT(&htim7);//关闭定时器,计时结束
			
			time = time_cnt*1000 + TIM7->CNT / 2; //统计声(单次)音返回时间
			
			Distence = 340*100* time /1000000; //微妙转化为秒
			if(Distence == 0)  Distence = last_distence;
			else               last_distence = Distence;
				
		}
	}
	
}

配合pid 控制函数,使用超声波测距的距离,进行避障,小车达到避障距离就开始转弯避障,(避障距离建议超过小车直径即可)



int Balance_Pwm,Vertical_Pwm,Trun_Pwm;
int Motor1,Motor2;
int Encoder_left,Encoder_right;
float Movement = 0; //目标速度
int CCD; //目标角度

int FS_MODE;        //0 遥控模式1,避障模式  2巡线模式
int Distence;       //检测小车与障碍物的距离


//PID控制任务
void Car_Task_100HZ(void)
{
	//启动超声波检测模式
	HC_SRC04_Start();
	
	
	//1、确定直立环的PWM
	
	Balance_Pwm = Vertical_Ring_PD(OutMpu.pitch,OutMpu.gyro_x); //*形参:(float Angle):x轴的角度/(float Gyro):x轴的角速度
	
	//2、确定速度环的PWM
	Encoder_left = Read_Encoder(1); //左轮
	Encoder_right = -Read_Encoder(2); //右轮
	
	Vertical_Pwm = Vertical_speed_PI(Encoder_left,Encoder_right,OutMpu.pitch,Movement);
	//3、确定方向环的PWM
	
	if(FS_MODE == 0) //遥控模式
		Trun_Pwm = Vertical_turn_PD(CCD,OutMpu.yaw);
	else if(FS_MODE == 1) //避障模式
	{
		if(Distence < 20) //直线距离在20cm 直接转向 20度
			Trun_Pwm = Vertical_turn_PD(20,OutMpu.yaw); //直接转向 20度
		else
			Trun_Pwm = 0; //直行
			
	}
	else if(FS_MODE == 2) //巡线模式
	{
		
	}
	
	
	
	//4、确定最终电机的左右pwm 
	
	Motor1 = Balance_Pwm+Vertical_Pwm+Trun_Pwm;
	Motor2 = Balance_Pwm+Vertical_Pwm-Trun_Pwm;
	
	PWM_Limiting((int) Motor1,(int) Motor2);

	//4、设置电机
	Set_PWM(Motor1,Motor2); //*形参;(int motor1):电机1对应的PWM值/(int motor2):电机2对应的PWM值
			
}
			
}

二、TSL1401 CCD摄像头实现小车巡线功能

巡线就是小车按照轨迹进行一定的运动,我们通过设计头模块进项轨迹实施采集分析调整车辆运动方向。

在这里插入图片描述

TSL1401 CCD摄像头?

参考案例 STM32版CCD线性摄像头寻线寻迹小车

在这里插入图片描述
在这里插入图片描述
CCD采集到外部像素,感应光线的强度通过AD 输出

在这里插入图片描述
不同的光线强度的灰度值是不一样的,通过信号传输给控制器,控制器根据灰度值来确定我们的条件。
在这里插入图片描述
上图所从左到右,由亮变暗在变亮,中间黑色区域是最暗大的,我们使用线性 就是横排采集一条线,判断内容。

在这里插入图片描述
上图看到SI由采集的到最后AD输出的电压值。
在这里插入图片描述

巡线小车功能开发(编写程序)

电路图如下:

在这里插入图片描述
在这里插入图片描述
CCD查看使用教程

TSL1401 示例代码如下:
/**
  ******************************************************************************
  * File Name          : main.c
  * Description        : Main program body
  ******************************************************************************
  ** This notice applies to any and all portions of this file
  * that are not between comment pairs USER CODE BEGIN and
  * USER CODE END. Other portions of this file, whether 
  * inserted by the user or by software development tools
  * are owned by their respective copyright owners.
  *
  * COPYRIGHT(c) 2018 STMicroelectronics
  *
  * Redistribution and use in source and binary forms, with or without modification,
  * are permitted provided that the following conditions are met:
  *   1. Redistributions of source code must retain the above copyright notice,
  *      this list of conditions and the following disclaimer.
  *   2. Redistributions in binary form must reproduce the above copyright notice,
  *      this list of conditions and the following disclaimer in the documentation
  *      and/or other materials provided with the distribution.
  *   3. Neither the name of STMicroelectronics nor the names of its contributors
  *      may be used to endorse or promote products derived from this software
  *      without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  ******************************************************************************
  */

/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "stm32f0xx_hal.h"
#include "adc.h"
#include "usart.h"
#include "gpio.h"

/* USER CODE BEGIN Includes */
#include "stdio.h"
#include <math.h>

#define CCD_MIDDLE 			0
#define CCD_THRESHOLD		1
#define CCD_LEFT				2
#define CCD_RIGHT				3

#define TSL_CLK_H HAL_GPIO_WritePin(GPIOA,GPIO_PIN_6,GPIO_PIN_SET) 
#define TSL_CLK_L HAL_GPIO_WritePin(GPIOA,GPIO_PIN_6,GPIO_PIN_RESET)

#define TSL_SI_H HAL_GPIO_WritePin(GPIOA,GPIO_PIN_7,GPIO_PIN_SET) 
#define TSL_SI_L HAL_GPIO_WritePin(GPIOA,GPIO_PIN_7,GPIO_PIN_RESET) 
#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif
PUTCHAR_PROTOTYPE
{
HAL_UART_Transmit(&huart1 , (uint8_t *)&ch, 1, 0xFFFF);
return ch;
}
/* USER CODE END Includes */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */
/* Private variables ---------------------------------------------------------*/

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);

/* USER CODE BEGIN PFP */
/* Private function prototypes -----------------------------------------------*/

/* USER CODE END PFP */

/* USER CODE BEGIN 0 */
uint16_t ADV[128]={0};  
uint8_t i;



struct tCCD
{
	uint16_t middle;      //中间位置值
	uint16_t threshold;   //像素ad阈值
	uint16_t left;        //左跳变的位置
	uint16_t right;       //右跳变的位置
};

struct tCCD  CCD;
/**************************************************************************************************
*函数名:abs()
*功能:将数进行绝对值处理
*形参:number 需要进行绝对值处理的数
*返回值:经过处理后绝对值
**************************************************************************************************/
int abs (int number)
{
	return( number>=0 ? number : -number );
}
/**************************************************************************************************
*函数名:Dly_us()
*功能:延时函数,用来调整CCD曝光
*形参:无
*返回值:无
***************************************************************************************************/
void Dly_us(void)
{
   int ii;    
   for(ii=0;ii<220;ii++);      
}
/**************************************************************************************************
*函数名:Get_Adc()
*功能:得到CCD数据
*形参:无
*返回值:读取到的电压值
***************************************************************************************************/
uint16_t Get_Adc(void)
{
	HAL_ADC_Start(&hadc);
	HAL_ADC_PollForConversion(&hadc, 50);
	if(HAL_IS_BIT_SET(HAL_ADC_GetState(&hadc), HAL_ADC_STATE_REG_EOC))
	return 	HAL_ADC_GetValue(&hadc);
		else
			while(1);
}
/***************************************************************************************************
*函数名:RD_TSL()
*功能:读取CCD原始数据
*形参:无
*返回值:无
****************************************************************************************************/
 void RD_TSL(void) 
 {
	uint8_t i=0,tslp=0;
	TSL_CLK_H;
	TSL_SI_L;
	Dly_us();
	 
	TSL_SI_H;
	TSL_CLK_L;
	Dly_us();
	 
	TSL_CLK_H;
	TSL_SI_L;
	Dly_us();
	  for(i=0;i<128;i++){
			TSL_CLK_L;
			Dly_us();
			ADV[tslp]=(Get_Adc())>>4;
			++tslp;
			TSL_CLK_H;
			Dly_us();
		}			
 }
 /************************************************************************************************************************
 *函数名:Find_Middle_CCD()
 *功能:读取CCD中值
 *形参:无
 *返回值:CCD中值位置
 *************************************************************************************************************************/
 uint8_t Find_CCD_DATA(void)
 {
	 static uint8_t i,j;
	 //static uint8_t Last_Middle_CCD;
	 uint8_t Middle_CCD_Value;
	 static uint16_t  value1_max,value1_min;
	 value1_max=ADV[0];
	 //读取最大值
		for(i=5;i<123;i++){
        if(value1_max<=ADV[i])
        value1_max=ADV[i];
     }
	 value1_min=ADV[0]; 
		 //得到最小值
		for(i=5;i<123;i++){
       if(value1_min>=ADV[i])
       value1_min=ADV[i];
     } 
		//计算阈值
	 CCD.threshold=(value1_max+value1_min)/2;
		 //计算左跳变值
		for(i = 5;i<118; i++){
		if(ADV[i]>CCD.threshold&&ADV[i+1]>CCD.threshold&&ADV[i+2]>CCD.threshold&&ADV[i+3]<CCD.threshold&&ADV[i+4]<CCD.threshold&&ADV[i+5]<CCD.threshold){	
		  CCD.left=i;
		  break;	
		}
	}
		//计算右跳变值
		for(j = 118;j>5; j--){
		if(ADV[j]<CCD.threshold&&ADV[j+1]<CCD.threshold&&ADV[j+2]<CCD.threshold&&ADV[j+3]>CCD.threshold&&ADV[j+4]>CCD.threshold&&ADV[j+5]>CCD.threshold){	
		  CCD.right=j;
		  break;	
		}
  }
		//计算中值
		CCD.middle =(CCD.right+CCD.left)/2;
//		if(abs(Middle_CCD_Value-Last_Middle_CCD)>70){
//			Middle_CCD_Value=Last_Middle_CCD;
//			Last_Middle_CCD=Middle_CCD_Value;
//		}
		return Middle_CCD_Value;
 }
 
 /***************************************************************************************************************************/
 void ANO_Send_Data(void)
{
		 HAL_UART_Transmit(&huart1,(uint8_t *)&CCD,sizeof(CCD),0xFFFF); 
}

/* USER CODE END 0 */

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_ADC_Init();
  MX_USART1_UART_Init();

  /* USER CODE BEGIN 2 */
	
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
  /* USER CODE END WHILE */
		//每5ms发送一帧数据
		HAL_Delay(5);
		//读取CCD原始数据,读取128个像素,
		RD_TSL();
		//对CCD数据进行处理(求阈值、中值、左右跳变值)
		Find_CCD_DATA();
		//通过串口将封装好的CCD数据向外发送
		ANO_Send_Data();
  /* USER CODE BEGIN 3 */

  }
  /* USER CODE END 3 */

}

/** System Clock Configuration
*/
void SystemClock_Config(void)
{

  RCC_OscInitTypeDef RCC_OscInitStruct;
  RCC_ClkInitTypeDef RCC_ClkInitStruct;
  RCC_PeriphCLKInitTypeDef PeriphClkInit;

    /**Initializes the CPU, AHB and APB busses clocks 
    */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI|RCC_OSCILLATORTYPE_HSI14;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.HSI14State = RCC_HSI14_ON;
  RCC_OscInitStruct.HSICalibrationValue = 16;
  RCC_OscInitStruct.HSI14CalibrationValue = 16;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL12;
  RCC_OscInitStruct.PLL.PREDIV = RCC_PREDIV_DIV1;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

    /**Initializes the CPU, AHB and APB busses clocks 
    */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

  PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART1;
  PeriphClkInit.Usart1ClockSelection = RCC_USART1CLKSOURCE_PCLK1;
  if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

    /**Configure the Systick interrupt time 
    */
  HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);

    /**Configure the Systick 
    */
  HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);

  /* SysTick_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @param  None
  * @retval None
  */
void _Error_Handler(char * file, int line)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  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

/**
  * @}
  */ 

/**
  * @}
*/ 

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

项目工程开始

工程CCD工程 ,将工程下载到051 开发板进行数据采集发送
在这里插入图片描述

F407,串口3 使能,开启中断
在这里插入图片描述

开启DMA 接受数据
在这里插入图片描述
导出工程即可打开工程文件
定义ccd
在这里插入图片描述

main 函数使用

//启动CCD数据接受
HAL_UART_Receive_DMA(&huart3, (uint8_t)&CCD, sizeof(CCD));*


/**
  * @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_DMA_Init();
  MX_USART1_UART_Init();
  MX_TIM1_Init();
  MX_TIM2_Init();
  MX_TIM4_Init();
  MX_TIM5_Init();
  MX_TIM7_Init();
  MX_USART3_UART_Init();
  /* USER CODE BEGIN 2 */
	
  printf("平衡小车开发项目\n");
	
	//启动PWM
	HAL_TIM_PWM_Start(&htim5, TIM_CHANNEL_3);
	HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_1);
	
	//启动编码器
	HAL_TIM_Encoder_Start(&htim1, TIM_CHANNEL_ALL);
	HAL_TIM_Encoder_Start(&htim2, TIM_CHANNEL_ALL);
	
	
	//启动CCD数据接受
	 HAL_UART_Receive_DMA(&huart3,  (uint8_t*)&CCD, sizeof(CCD));
	
  /* USER CODE END 2 */
	
 

  /* Call init function for freertos objects (in freertos.c) */
  MX_FREERTOS_Init();
  /* Start scheduler */
  osKernelStart();

  /* We should never get here as control is now taken by the scheduler */
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

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

设置中断检测初始化128像素后再次重新接受
在这里插入图片描述
下面可以打印了
在这里插入图片描述
通过CCD 获取摄像头捕捉的中间值得差值在64以内就可以让小车通过转弯来重新定义方向和位置
Trun_Pwm = Vertical_turn_PD(CCD.middle,OutMpu.yaw); //直接转向 20度

struct tCCD
{
uint16_t middle; //中间位置值
uint16_t threshold; //像素ad阈值
uint16_t left; //左跳变的位置
uint16_t right; //右跳变的位置
};

三、遥控小车

在这里插入图片描述

蓝牙控制

esp32 项目工程的使用

在这里插入图片描述
使用过esp8266 的就了解过esp32 是 8266 升级款。
前期有几期是关于Node mcu esp8266 物联网模块的

工作原理,小车F407 通过esp32 联网,和手机上位机软件进行数据交互请求,esp32 通过串口6 发送小车指令,来控制小车,前后,转向。

打开工程,使能串口6,开启中断
在这里插入图片描述

使用keil5 打开工程,配置串口接受esp32数据
FreeRTOS 任务里面配置数据的接收,其中使用到了队列管理xQueueCreate ,引入范例文件esp32 到工程,

void StartTask_Interaction(void const * argument)
{
  /* USER CODE BEGIN StartTask_Interaction */
	printf("交互进程运行\n");
	Message_Queue =  xQueueCreate (Message_Q_NUM,Message_Q_length);//创建一个队列

	HAL_UART_Receive_DMA(&huart6, Uart6_Rcv.RcvBuf, 255);设置串口接收
	__HAL_UART_ENABLE_IT(&huart6,UART_IT_IDLE); //使能中断

	ESP32_Init();
  /* Infinite loop */
  for(;;)
  {
		ESP32_Data_Rcv(); //接受数据
		Car_Task_Interaction(); //交互进程
    osDelay(1);
  }
  /* USER CODE END StartTask_Interaction */
}

esp32 使用例程模板

方法函数的介绍
ESP32_Init(); esp32 的初始化
ESP32_Send_Data() esp32 数据的发送
ESP32_Cmd_Rcv()命令的接受
ESP32_Data_Rcv()数据的接受
ESPSend_Cmd() 命令的发送
uart_data_send() 串口的发送

esp32.c


#include "esp32.h"
#include "usart.h"
#include <stdarg.h>
#include <string.h>
#include "connect.h"
#include "FreeRTOS.h"
#include "task.h"
#include "cmsis_os.h"


uint8_t NET_MODE = 0;             //0、蓝牙模式     1、wifi模式   默认蓝牙
uint8_t WIFI_CONNECT_FLAG = 0;    //WIFI连接标志位
uint8_t BLE_CONNECT_FLAG = 0;     //BLE连接标志位

tEsp32_RcvBuf Esp32Rcv;      //ESP数据接收缓冲区


tTimeDelay    ESP32_TimeDelay;


extern 	QueueHandle_t Message_Queue;

#define AT_CWSAP_STRING   "AT+CWSAP=\"FarsightESP32\",\"123456789\",5,3\r\n"
#define AT_BLEADVDATA_STRING    "AT+BLEADVDATA=\"0201060B09466172736967687420030302A0\"\r\n"

volatile tATCmd  ATCmds[20]=
{
  //*CmdSend,              *CmdRcv,    TimeOut,   CmdStatus,  
  {NULL,NULL,0,NO_RCV},
  {"AT\r\n",             "OK",         5000,      NO_RCV, },   //检测AT指令       
  {"AT+CIPAPMAC?\r\n",   "CIPAPMAC",   2000,      NO_RCV, },	 //获取MAC地址    
  {AT_CWSAP_STRING,       "CWSAP" ,    2000,      NO_RCV, },   //建立MAC相关的AP名称  
  {"AT+CWMODE=3\r\n",    "OK" ,        2000,      NO_RCV, },   //设置WIFI模式AP+Station
  {"AT+CIPMUX=1\r\n",    "OK" ,        2000,      NO_RCV, },   //设置多连接
  {"AT+CIPSERVER=1\r\n", "OK" ,        2000,      NO_RCV, },   //初始化TCP服务器 默认IP(192.168.4.1)默认端口号(333)
  {"AT+CIPSTO=0\r\n",    "OK" ,        2000,      NO_RCV, },   //设置TCP连接时间
  {"AT+CIPSEND=0\r\n",   "OK" ,        500,       NO_RCV, },   //TCP发送数据

	{"AT+RST\r\n",          "ready" ,    1000,      NO_RCV, },   //重启AT指令:
  {"AT+BLEINIT=2\r\n",   "OK" ,        1000,      NO_RCV, },   //初始化为 BLE server:
  {"AT+BLEADDR?\r\n",    "BLEADDR" ,   2000,      NO_RCV, },   //查询自身的 BLE 地址
  {AT_BLEADVDATA_STRING,  "OK" ,       2000,      NO_RCV, },   //配置广播数据包
  {"AT+BLEGATTSSRVCRE\r\n",  "OK",     1000,      NO_RCV, },   //创建服务:
  {"AT+BLEGATTSSRVSTART\r\n", "OK" ,   3000,      NO_RCV, },   //开启服务 
  {"AT+BLEADVSTART\r\n",   "OK" ,      1000,      NO_RCV, },   //开始广播
  {"AT+BLEGATTSNTFY\r\n" , ">" ,       500,       NO_RCV, },   //服务器发送数据
	
  {"CMDSTR_NOUSE",       "OK" ,        2000,      NO_RCV, }, 
};


void uart_data_send(uint8_t *fmt, uint16_t len)
{
	taskENTER_CRITICAL();  
	HAL_UART_Transmit(&huart6, (uint8_t *)fmt, len,100);
	taskEXIT_CRITICAL(); 
}


tCmdStatus ESPSend_Cmd(tATCmdNum ATCmdNum)
{		
		uint8_t len;
	
		//清空接收缓存以及接收状态

		ATCmds[ATCmdNum].CmdStatus = NO_RCV;
		
		//发送命令
		len = strlen(ATCmds[ATCmdNum].CmdSend);
		uart_data_send((uint8_t *)ATCmds[ATCmdNum].CmdSend, len);
		HAL_UART_Transmit(&huart1,(uint8_t *)ATCmds[ATCmdNum].CmdSend, len,100);

		
	 //打开超时定时器
	 SetTime(&ESP32_TimeDelay, ATCmds[ATCmdNum].TimeOut);
		 
	 while(ATCmds[ATCmdNum].CmdStatus != RCV_SUCCESS)
	 {
		 
			ESP32_Cmd_Rcv(ATCmdNum);
		  if(ATCmds[ATCmdNum].CmdStatus == RCV_TIMEOUT)
				return RCV_TIMEOUT;
	 }
	 
	 return RCV_SUCCESS;
}

/*发送数据函数*/
tCmdStatus ESP32_Send_Data(uint8_t *SendBuf,uint8_t len)
{
	  uint8_t buf[30] =  {0};
		tATCmdNum ATCmdNum;
	
		if(! (BLE_CONNECT_FLAG || WIFI_CONNECT_FLAG))  //未连接状态不能发送数据
		{
			DBG("未连接设备\n");
			return NO_CONNECT;
		}		
		
		if(NET_MODE == BLE_MODE)    //蓝牙模式
		{
			sprintf((char *)buf,"AT+BLEGATTSNTFY=%d,%d,%d,%d\r\n",0,1,2,len);
			ATCmdNum = AT_BLEGATTSNTFY;		
		}
		else 			//WIFI模式
		{
			sprintf((char *)buf,"AT+CIPSEND=%d,%d\r\n",0,len);
			ATCmdNum = AT_CIPSEND;
		}
			
		uart_data_send(buf,strlen((char *)buf));     //发送命令
		
		//打开超时定时器
	 ATCmds[ATCmdNum].CmdStatus = NO_RCV;        //清接收状态
	 SetTime(&ESP32_TimeDelay, ATCmds[ATCmdNum].TimeOut);
		 
	 while(ATCmds[ATCmdNum].CmdStatus != RCV_SUCCESS)
	 {		 
			ESP32_Cmd_Rcv(ATCmdNum);
		  if(ATCmds[ATCmdNum].CmdStatus == RCV_TIMEOUT)
				return RCV_TIMEOUT;
	 }
		
		uart_data_send( SendBuf,len);                //发送数据

		DBG("send data ok\n");
	 
	 return RCV_SUCCESS;

}



void ESP32_Cmd_Rcv(tATCmdNum ATCmdNum)
{
	memset(&Esp32Rcv,0,sizeof(Esp32Rcv));
	
	if(xQueueReceive(Message_Queue, &Esp32Rcv,0 ))
	{
				DBG("%s", Esp32Rcv.RcvBuf);
		
				
				//接收处理命令
				if(strstr((const char*)Esp32Rcv.RcvBuf,ATCmds[ATCmdNum].CmdRcv) != NULL)
				{
					ATCmds[ATCmdNum].CmdStatus = RCV_SUCCESS;						
				}			
	 
				//打开接收指示灯
				//SetLedRun(LED_RX);
				
				
	}
	else
	{
			if(CompareTime(&ESP32_TimeDelay))
			{
				ATCmds[ATCmdNum].CmdStatus = RCV_TIMEOUT;
			}
	}	

}

void ESP32_Data_Rcv(void)
{
	memset(&Esp32Rcv,0,sizeof(Esp32Rcv));
	if(xQueueReceive(Message_Queue, &Esp32Rcv,0 ))
	{
			 //接收处理数据(保护客户端发来的数据,还有其他调试数据)			

				DBG("%s", Esp32Rcv.RcvBuf);
		
			 if(NET_MODE == BLE_MODE)    //蓝牙模式
			 {
						
						if(strstr((char *)(Esp32Rcv.RcvBuf),"WRITE") != NULL ) //收到客户端数据
						{
								DBG("收到上位机数据\n");
				
								BLE_CONNECT_FLAG = 1;   //对方打开读写特征值时,置连接标志
							
							
								//提取处理数据;
								EP32_RcvData_Extract(Esp32Rcv.RcvBuf,Esp32Rcv.DataLen);	
												
								return ;			          				
						}
						
						if(strstr((char *)(Esp32Rcv.RcvBuf),"BLEDISCONN") != NULL) //客户端断开连接
					 {
							 DBG("蓝牙断开连接,重新广播\n");	
						 
							 BLE_CONNECT_FLAG = 0;    //清除连接标志位
								//重新广播
							 ESPSend_Cmd(AT_BLEADVDATA);
							 ESPSend_Cmd(AT_BLEADVSTART);
					 }

		  }
			else     //WIFI模式
			{
				
				   if((!WIFI_CONNECT_FLAG) && (strstr((char *)(Esp32Rcv.RcvBuf),"CONNECT")!=NULL )) //收到客户端数据
					 {
							DBG("WIFI已连接\n");	
						 
							WIFI_CONNECT_FLAG = 1;   //置连接标志位
					 }

			
				
					if(strstr((char *)(Esp32Rcv.RcvBuf),"+IPD") != NULL  ) //收到客户端数据
				  {
				 		DBG("WIFI收到上位机数据\n");	
						
						//提取并处理数据;
						EP32_RcvData_Extract(Esp32Rcv.RcvBuf,Esp32Rcv.DataLen);	
											
						return ;						
					
				  }	

					if(strstr((char *)(Esp32Rcv.RcvBuf),"CLOSED") != NULL) //客户端断开连接
					 {
							 DBG("WIFI断开连接\n");	
						 
							 WIFI_CONNECT_FLAG = 0;    //清除连接标志位

					 }	
				
			}
			
			
	}
}
	



void ESP32_Init(void)
{
		tATCmdNum i = AT_IDIE;
	
		if(NET_MODE == BLE_MODE)                //蓝牙模式初始化
		{
			for(i = AT_BLEINIT; i<=AT_BLEADVSTART ; i++)
			{
				if( ESPSend_Cmd(i) != RCV_SUCCESS)
				{				
					DBG("PES32 Init failed\n");
					return ;
				}
				
			}
		}
		else                                  //WIFI模式初始化
		{
					for(i = AT; i<=AT_CIPSTO ; i++)
					{						
						if( ESPSend_Cmd(i) != RCV_SUCCESS)
						{
							DBG("PES32 Init failed\n");
							return ;
						}
					}
					
						DBG("PES32 Init Success\n");
		}
}

esp32.h

#ifndef __ESP32_H
#define __ESP32_H

#include <stdint.h>
#include <stdio.h>
#include "delay.h"

extern uint8_t Esp32_RcvBuf [255];
extern uint8_t Esp32_RBuffLen ; 

//#define DEBUG 

#ifdef DEBUG 
#define DBG(x...)   printf(x) 
#else 
#define DBG(x...) 
#endif  



#define  BLE_MODE   0
#define  WIFI_MODE  1



//AT命令序列号
typedef enum
{
	AT_IDIE  = 0,
	AT,
	AT_CIPAPMAC,
	AT_CWSAP,
	AT_CWMODE,
	AT_CIPMUX,
	AT_CIPSERVER,
	AT_CIPSTO,
	AT_CIPSEND,
	
	AT_RST,
	AT_BLEINIT,
	AT_BLEADDR,
	AT_BLEADVDATA,
	AT_BLEGATTSSRVCRE,
	AT_BLEGATTSSRVSTART,
	AT_BLEADVSTART,
	AT_BLEGATTSNTFY,
	CMDSTR_NOUSE,

}tATCmdNum;


//命令返回结果的状态
typedef enum{
	NO_RCV  = 0,
	RCV_SUCCESS,
	RCV_TIMEOUT,
	NO_CONNECT,
}tCmdStatus;


typedef struct{
	char *CmdSend;         //发送的命令
	char *CmdRcv; 	       //正确返回包含的字符串
	uint16_t TimeOut;      //超时的时间
	tCmdStatus CmdStatus;  //命令返回的状态
}tATCmd; 

typedef struct {
	uint8_t DataLen;
	uint8_t RcvBuf  [255];
}tEsp32_RcvBuf;

extern tTimeDelay    ESP32_TimeDelay; 

extern uint8_t Esp32_RcvBuf  [255];
extern uint8_t ESP32_RCV_FLAG;
extern uint8_t NET_MODE ; 
extern uint8_t WIFI_CONNECT_FLAG ;    //WIFI连接标志位
extern uint8_t BLE_CONNECT_FLAG ;     //BLE连接标志位


void ESP32_Cmd_Rcv(tATCmdNum ATCmdNum);
tCmdStatus ESP32_Send_Data(uint8_t *SendBuf,uint8_t len);
void ESP32_Init(void);
void ESP32_Data_Rcv(void);

#endif /*__ESP32_H*/


通讯协议的制定

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  1. 引入范例文件connect 到工程,

通讯协议定制库函数文件(范例)

connect.h

#ifndef __CONNECT_H
#define __CONNECT_H

#include <stdint.h>


//功能宏定义
#define MOVEMENT              0X01         //小车动作
#define READINFO              0X02         //读取PID、模式、版本,恢复默认参数
#define SETMODE               0X03         //设置小车工作模式
#define WRITEPID1             0X10         //设置PID1
#define WRITEPID2             0X11         //设置PID2

//小车动作宏定义
#define CAR_STOP              0X00         //停止
#define CAR_FORWARD           0X01         //前进
#define CAR_BACK              0X02         //后退
#define CAR_TURN_LEFT         0X03         //左转
#define CAR_TURN_RIGHT        0X04         //右转


//小车工作模式定义
#define REMOTE_MODE           0X01        //遥控模式
#define	LINE_TRACK_MODE       0X02        //巡线模式
#define AVOID_MODE            0X03        //蔽障模式


//读取小车信息定义
#define READ_ALL_ARG          0X00        //读取所有的数据
#define READ_PID              0X01        //读取PID数据
#define	READ_WORK_MODE        0X02        //读取当前工作模式
#define	READ_VERINFO          0XA0        //读取版本信息
#define	RESTORE_DEFAULT_ARG   0XA1        //恢复默认参数


//定义版本相关信息
#define Hardware_Type           10
#define Hardware_VER            10
#define Software_VER            10
#define Protocol_VER            10

//定义小车上报数据的功能类型
#define CAR_VER										0x00					//版本信息
#define CAR_POSE								  0x01					//姿态
#define CAR_SENSER								0x02					//传感器原始数据
#define CAR_RCDATA								0x03					//小车接收到的遥控数据
#define CAR_POWER									0x04					//小车电量
#define CAR_MOTO									0x05					//电机转速
#define CAR_SENSER2								0x06					//超声波距离
#define CAR_MOD										0X07					//小车模式
#define CAR_PID_1									0x10					//PID1数据
#define CAR_PID_2									0x11					//PID2的数据
#define CAR_PID_3									0X12					//PID3的数据
#define CAR_CCD_SEN								0XF1					//CCD的数据 
#define CAR_User_Waveform					0xA1	




typedef struct 
{
		u8 send_check;
		u8 send_version;
		u8 send_status;
		u8 send_senser;
		u8 send_senser2;
		u8 send_pid1;
		u8 send_pid2;
		u8 send_pid3;
		u8 send_pid4;
		u8 send_pid5;
		u8 send_pid6;
		u8 send_rcdata;
		u8 send_offset;
		u8 send_motopwm;
		u8 send_power;
		u8 send_user;
		u8 send_speed;
		u8 send_location;

}dt_flag_t;

u8 EP32_RcvData_Extract(const uint8_t *Buff,int len);
void Connect_Send_data(u8 CMD_Data);

#endif


connect.c

#include "inv_mpu_user.h"
#include "connect.h"
#include "string.h"
#include "stdio.h"
#include "esp32.h"
#include "car_task.h"
#include "contrl.h"

/**************************************************************************************************************
*函数名:package_report_data()
*功能:添加帧头功能字及校验位
*形参:(u8 fun):功能字/(u8*data):要发送的数据包/(u8 len):长度
*返回值:无
**************************************************************************************************************/
static void package_report_data(u8 fun,u8*data,u8 len)
{
    static u8 send_buf[40]={0};   //添加static,给栈减小一点压力
		u16 check_sum=0;
    u8 i;
		
		
		//封装协议头
    if(len>28)return;   
    send_buf[0]=0XAA;  
		send_buf[1]=0XAA; 
    send_buf[2]=fun;    
    send_buf[3]=len; 

		//封装数据
    for(i=0;i<len;i++)send_buf[4+i]=data[i];

		//计算校验值
    for(i=0;i<len+4;i++)	check_sum+=send_buf[i];	
		send_buf[len+4]=((check_sum)&0xFF);
		
		//发送数据
		ESP32_Send_Data(send_buf,len+5);
}

/*********************************************************************************************************
*函数名:ANO_VER()
*功能:发送版本信息
*形参:HardwareType,HardwareVER,SoftwareVER,ProtocolVER
*返回值:无
*********************************************************************************************************/
static void ANO_VER(u8 HardwareType,u16 HardwareVER,u16 SoftwareVER,u16 ProtocolVER)
{
		static u8 tbuf[7];   
		tbuf[0]=(HardwareType)&0XFF;
	
		tbuf[1]=((HardwareVER)>>8)&0XFF;
		tbuf[2]=(HardwareVER)&0XFF;
	
		tbuf[3]=((SoftwareVER)>>8)&0XFF;
		tbuf[4]=(SoftwareVER)&0XFF;
	
		tbuf[5]=((ProtocolVER)>>8)&0XFF;
		tbuf[6]=(ProtocolVER)&0XFF;
		package_report_data(CAR_VER,tbuf,7);
}

/*************************************************************************************************************
*函数名:ANO_MOD()
*功能:发送当前模式
*形参:data模式
*返回值:无
*************************************************************************************************************/
static void ANO_MOD(u8 data)
{
	u8 tbuf[1];
	tbuf[0]=(data)&0XFF;  
	package_report_data(CAR_POSE,tbuf,1);
}
/**********************************************************************************************************
*函数名:ANO_CAR_POSE()
*功能:发送姿态
*形参:angle(x俯仰,y横滚,z偏航)
*返回值:无
***********************************************************************************************************/
static void ANO_CAR_POSE(void)
{
	static u8 tbuf[6]; 
	int32_t anglex,angley,anglez;
	anglex=((short)(OutMpu.pitch))*100;
	angley=((short)(OutMpu.roll))*100;
	anglez=((short)(OutMpu.yaw))*100;
	
	tbuf[0]=((anglex)>>8)&0XFF;      
	tbuf[1]=(anglex)&0XFF;
	tbuf[2]=((angley)>>8)&0XFF;
	tbuf[3]=(angley)&0XFF;
	tbuf[4]=((anglez)>>8)&0XFF;
	tbuf[5]=(anglez)&0XFF;
	package_report_data(CAR_POSE,tbuf,6);
}

/**************************************************************************************************************
*函数名:ANO_SENSER()
*功能:发送传感器原数据
*形参:acc:陀螺仪,gyro:加速度计,mag:电子罗盘
*返回值:无
**************************************************************************************************************/
static void ANO_SENSER(void)
{
	static u8 tbuf[18];    
	u8 accx,accy,accz,gyrox,gyroy,gyroz,magx,magy,magz;
	
	accx=(u8)((OutMpu.acc_x)*100);
	accy=(u8)((OutMpu.acc_y)*100);
	accz=(u8)((OutMpu.acc_z)*100);
	
	gyrox=(u8)((OutMpu.gyro_x)*100);
	gyroy=(u8)((OutMpu.gyro_x)*100);
	gyroz=(u8)((OutMpu.gyro_x)*100);
	
//	magx=(u8)((mag->x)*100);
//	magy=(u8)((mag->y)*100);
//	magz=(u8)((mag->z)*100);
	
	tbuf[0]=((accx)>>8)&0XFF;      
	tbuf[1]=(accx)&0XFF;
	tbuf[2]=((accy)>>8)&0XFF;
	tbuf[3]=(accy)&0XFF;
	tbuf[4]=((accz)>>8)&0XFF;
	tbuf[5]=(accz)&0XFF;
	
	tbuf[6]=((gyrox)>>8)&0XFF;      
	tbuf[7]=(gyrox)&0XFF;
	tbuf[8]=((gyroy)>>8)&0XFF;
	tbuf[9]=(gyroy)&0XFF;
	tbuf[10]=((gyroz)>>8)&0XFF;
	tbuf[11]=(gyroz)&0XFF;
	
	tbuf[12]=((magx)>>8)&0XFF;      
	tbuf[13]=(magx)&0XFF;
	tbuf[14]=((magy)>>8)&0XFF;
	tbuf[15]=(magy)&0XFF;
	tbuf[16]=((magz)>>8)&0XFF;
	tbuf[17]=(magz)&0XFF;
	package_report_data(CAR_SENSER,tbuf,18);
}
/*************************************************************************************************************
*函数名:ANO_PID()
*功能:发送PID数据
*形参:PID1,PID2,PID3的参数
*返回值:无
**************************************************************************************************************/
static void ANO_PID(u8 Function)
{
  static u8 tbuf[18];
	int16_t	PID1_P,PID1_I,PID1_D,PID2_P,PID2_I,PID2_D,PID3_P,PID3_I,PID3_D;
	
	PID1_P=(u16)((PID.Balance_Kp)*100);
	PID1_I=(u16)((PID.Balance_Ki)*100);
	PID1_D=(u16)((PID.Balance_Kd)*100);
	
	PID2_P=(u16)((PID.Velocity_Kp)*100);
	PID2_I=(u16)((PID.Velocity_Ki)*100);
	PID2_D=(u16)((PID.Velocity_Kd)*100);
	
	PID3_P=(u16)((PID.Turn_Kp)*100);
	PID3_I=(u16)((PID.Turn_Ki)*100);
	PID3_D=(u16)((PID.Turn_Kd)*100);
	
	tbuf[0]=((PID1_P)>>8)&0XFF;      
	tbuf[1]=(PID1_P)&0XFF;
	tbuf[2]=((PID1_I)>>8)&0XFF;
	tbuf[3]=(PID1_I)&0XFF;
	tbuf[4]=((PID1_D)>>8)&0XFF;
	tbuf[5]=(PID1_D)&0XFF;
		
	tbuf[6]=((PID2_P)>>8)&0XFF;      
	tbuf[7]=(PID2_P)&0XFF;
	tbuf[8]=((PID2_I)>>8)&0XFF;
	tbuf[9]=(PID2_I)&0XFF;
	tbuf[10]=((PID2_D)>>8)&0XFF;
	tbuf[11]=(PID2_D)&0XFF;
	
	tbuf[12]=((PID3_P)>>8)&0XFF;      
	tbuf[13]=(PID3_P)&0XFF;
	tbuf[14]=((PID3_I)>>8)&0XFF;
	tbuf[15]=(PID3_I)&0XFF;
	tbuf[16]=((PID3_D)>>8)&0XFF;
	tbuf[17]=(PID3_D)&0XFF;
	if(Function==1)
		package_report_data(CAR_PID_1,tbuf,18);
	if(Function==2)
		package_report_data(CAR_PID_2,tbuf,18);
	if(Function==3)
		package_report_data(CAR_PID_1,tbuf,18);
}

/**************************************************************************************************************
*函数名:ANO_CCD_SEN()
*功能:发送CCD数据
*形参:ccd:CCD数据包括(阈值,中值,左跳变,右跳变)
*返回值:无
***************************************************************************************************************/
void	ANO_CCD_SEN(void)
{
	static u8 tbuf[8];
	u16 CCD_MIDDLE,CCD_THRESHOLD,CCD_LEFT,CCD_RIGHT;
	
	CCD_MIDDLE=CCD.middle;
	CCD_THRESHOLD=CCD.threshold;
	CCD_LEFT=CCD.left;
	CCD_RIGHT=CCD.right;
	
	tbuf[0]=((CCD_MIDDLE)>>8)&0XFF;
	tbuf[1]=(CCD_MIDDLE)&0XFF;
	
	tbuf[2]=((CCD_THRESHOLD)>>8)&0XFF;
	tbuf[3]=(CCD_THRESHOLD)&0XFF;
	
	tbuf[4]=((CCD_LEFT)>>8)&0XFF;
	tbuf[5]=(CCD_LEFT)&0XFF;
	
	tbuf[6]=((CCD_RIGHT)>>8)&0XFF;
	tbuf[7]=(CCD_RIGHT)&0XFF;
	
	package_report_data(CAR_CCD_SEN,tbuf,8);
}

/*************************************************************************************************************
*函数名:ANO_POWER()
*功能:发送电量
*形参:data:电量(0%,25%,50%,75%,100%)
*返回值:
**************************************************************************************************************/
void ANO_POWER(u16 data)
{
	static u8 tbuf[2];
	tbuf[0]=((data)>>8)&0XFF;      
	tbuf[1]=(data)&0XFF;
	package_report_data(CAR_POWER,tbuf,2);
}
/*************************************************************************************************************
*函数名:ANO_MOTO()
*功能:发送电机转速
*形参:PWM_MOTO:电机转速
*返回值:无
**************************************************************************************************************/
void ANO_MOTO(float PWM_MOTO)
{
	u16 PWM_Percentage;
	static u8 tbuf[2];
	
	PWM_Percentage=fabs(PWM_MOTO)*1.24;
	
	tbuf[0]=((PWM_Percentage)>>8)&0XFF;      
	tbuf[1]=(PWM_Percentage)&0XFF;
	package_report_data(CAR_MOTO,tbuf,2);
}
/**************************************************************************************************************
*函数名:Connect_Send_data()
*功能:发送数据给上位机
*形参:
*返回值:无
**************************************************************************************************************/
void Connect_Send_data(u8 CMD_Data)
{
	
		  switch(CMD_Data)
			{
				case READ_ALL_ARG:
				//			DBG("\r发送数据到上位机\n");
	
//							ANO_VER(Hardware_Type,Hardware_VER,Software_VER,Protocol_VER);
//							ANO_MOD(FS_MODE);
							ANO_CAR_POSE();
							ANO_SENSER();
//							ANO_RCDATA(F_CMD,Movement);
//							ANO_POWER(power);
							ANO_MOTO(Encoder_right);
//							ANO_SENSER2(Distance);
							ANO_CCD_SEN();
				
					break;
				case READ_PID:
						DBG("\r发送PID请求\n");
				
						ANO_PID(1);
					break;
				case READ_WORK_MODE:
					  DBG("\r发送当前模式\n");
					  ANO_MOD(FS_MODE);
					break;
				case READ_VERINFO:
					DBG("\r发送版本信息\n");
				  
				  ANO_VER(Hardware_Type,Hardware_VER,Software_VER,Protocol_VER);
					break;
				default:
					break;
			}

}




/**************************************************************************************************************
*函数名:Host_Data_Receive_Anl()
*功能:上位机数据包解析
*形参:(u8 *data_buf):缓存区中的接收到的数据/(u8 num):数据包长度
*返回值:无
**************************************************************************************************************/
void Host_Data_Receive_Anl(u8 *data_buf,u8 num)
{
	u8 sum = 0,i;
	u8 function = *(data_buf+2),cmd = *(data_buf+4);

	
	//从帧头道数据位的计算校验;
	DBG("收到有效数据:");
	for(i=0;i<(num-1);i++)
	{
		
		DBG("%x  ",*(data_buf+i));
		sum += *(data_buf+i);
	}
	DBG("\r\n");
	
	校验不过直接返回
	if(sum != *(data_buf+num-1) )		return;		
	//判断帧头
	if(!(*(data_buf)==0xAA && *(data_buf+1)==0xAF))		return;	

	
	switch(function)
	{
		case MOVEMENT: 	
			DBG("设置小车动作\n");
		
		  switch(cmd)
			{
				case CAR_STOP:
					DBG("小车停止\n");
				
					if(FS_MODE==0){
						Contrl_Turn=64;
						Movement=0;
					}
								
					break;
				case CAR_FORWARD:
					DBG("小车前进\n");
				
					if(FS_MODE==0){
						Contrl_Turn=64;
						Movement=50;
					}
										
					break;
				case CAR_BACK:
					DBG("小车后退\n");

					if(FS_MODE==0){
						Contrl_Turn = 64;
						Movement = -50;
					}
					
					break;
				case CAR_TURN_LEFT:
					DBG("小车左转\n");

					if(FS_MODE==0){
						Contrl_Turn=30;
						Movement=0;
					}
					
					break;
				case CAR_TURN_RIGHT:
					DBG("小车右转\n");

					if(FS_MODE==0){
						Contrl_Turn=98;
						Movement=0;
					}
					
					break;
				default:
					break;
			}
			break;
			
		case READINFO: 
			DBG("读取小车基本信息\n");

		  switch(cmd)
			{
				case READ_PID:
					DBG("读取PID数据\n");
				
					break;
				case READ_WORK_MODE:
					DBG("读取当前工作模式\n");
				
					break;
				case READ_VERINFO:
					DBG("读取版本信息\n");
				
					break;
				case RESTORE_DEFAULT_ARG:
					DBG("恢复默认参数\n");
				
						PID.Balance_Kp=200;
						PID.Balance_Kd=1;
						PID.Velocity_Kp=-60;
						PID.Velocity_Ki=-0.3;
						PID.Turn_Kp = 18;
						PID.Turn_Kd = 0.18;

					break;			
				default:
					break;
			}	
		
			break;
		case SETMODE: 
			DBG("设置小车工作模式\n");
			
		  switch(cmd)
			{
				case REMOTE_MODE:
					DBG("遥控模式\n");
					
					FS_MODE = 0;
				
					break;
				case AVOID_MODE:
					DBG("避障模式\n");
				
					FS_MODE = 1;
					break;
				case LINE_TRACK_MODE:
					DBG("巡线模式\n");
				
					FS_MODE = 2;
				
					break;

				default:
					break;
			}		
		
			break;
		case WRITEPID1: 
			//上位机发送9个的16位数,*100后发送过来的,
			DBG("设置小车PID1\n");
		
			PID.Balance_Kp  = 0.01*( (vs16)(*(data_buf+4)<<8)|*(data_buf+5) );
			PID.Balance_Ki  = 0.01*( (vs16)(*(data_buf+6)<<8)|*(data_buf+7) );
			PID.Balance_Kd  = 0.01*( (vs16)(*(data_buf+8)<<8)|*(data_buf+9) );
			
			PID.Velocity_Kp = 0.01*( (vs16)(*(data_buf+10)<<8)|*(data_buf+11) );
			PID.Velocity_Ki = 0.01*( (vs16)(*(data_buf+12)<<8)|*(data_buf+13) );
			PID.Velocity_Kd = 0.01*( (vs16)(*(data_buf+14)<<8)|*(data_buf+15) );
			
			PID.Turn_Kp 	= 0.01*( (vs16)(*(data_buf+16)<<8)|*(data_buf+17) );
			PID.Turn_Ki 	= 0.01*( (vs16)(*(data_buf+18)<<8)|*(data_buf+19) );
			PID.Turn_Kd	  = 0.01*( (vs16)(*(data_buf+20)<<8)|*(data_buf+21) );

			break;
		case WRITEPID2: 
			DBG("设置小车PID2\n");
		
			break;
		default:
			break;
		
	}
	
}


/**********************************************************************************************************
*函数名:EP32_RcvData_Extract
*功能:提取ESP32中有效数据,并且将其拷贝到缓存区中
*形参:中断接收的数据
*返回值:成功返回0,失败返回1
注:数据格式
							命令  帧头  帧头  功能  长度  数据  校验
例:          前进  0xaa  0xaf  0x01  0x01  0x01  计算
***********************************************************************************************************/

u8 EP32_RcvData_Extract(const uint8_t *Buff,int len)
{	
	static u8 RxBuffer[50];
	static int data_len = 0,data_cnt = 0;
	static u8 state = 0;
	u8 i, data;
	
	
	for(i=0;i<len ;i++){
		  
		  data = Buff[i];
		
		   //判断帧头		
			if(state==0&&data==0xAA)
			{
				state=1;
				RxBuffer[0]=data;
			}
			else if(state==1&&data==0xAF)
			{
				state=2;
				RxBuffer[1]=data;
			}
			//功能字截止到0XF1
			else if(state==2&&data<0XF1)
			{
				state=3;
				RxBuffer[2]=data;
			}
			//数据长度小于50
				else if(state==3&&data<50)
			{
				state = 4;
				RxBuffer[3]=data;
				data_len = data;
				data_cnt = 0;
			}
			//将数据复制到RxBuffer中
			else if(state==4&&data_len>0)
			{
				data_len--;
				RxBuffer[4+data_cnt++]=data;
				if(data_len==0)
					state = 5;
			}
			//计算校验值
			else if(state==5)
			{
				state = 0;
				RxBuffer[4+data_cnt]=data;
				Host_Data_Receive_Anl(RxBuffer,data_cnt+5);
				memset(RxBuffer,0,50);
				return 0;
			}

	}
	return 1;
}

oled 显示置入屏幕显示小车状态

096 oled 已经不陌生了,都已经使用很多次了。iic 数据传输。
在这里插入图片描述

  1. oled 显示电路

在这里插入图片描述
2. 按键原理图
在这里插入图片描述

  1. 蜂鸣器 电气原理图
    在这里插入图片描述

  2. 电池adc接口 电气原理图

在这里插入图片描述

开始配置工程

cube工程配置端口

在这里插入图片描述
输出数据到oled 屏幕

在这里插入图片描述
控制板需要接收按键按下电平,所以设置输入模式

在这里插入图片描述
oled 管脚设置输出模式
在这里插入图片描述

蜂鸣器一样设置输出模式

ok 导出工程打开keil5 开始编辑程序

四、stm32项目平衡车项目下载源码

本文章学习借鉴于创客学院团队,以表感谢。教学视频
资源是免费下载的,点击下载即可


上一节,stm32项目平衡车详解(stm32F407)上

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

stm32项目平衡车详解(stm32F407)下 的相关文章

随机推荐

  • 利用递归求斐波那契数列

    斐波那契额是二级递推数列 从第三项起 每项为前两项的和 利用函数的递归可以很好实现 只需每次返回n 1 和 n 2 即可 上代码 define CRT SECURE NO WARNINGS 1 include
  • C#使用多线程并发之异步委托

    阻塞式编程 我们一边编写的代码都是同步代码 也就是从上到下按照顺序执行 例如 public delegate void DoWorkHandler class Program static void Main string args Con
  • vsflexgrid单元格换行后自动使用行高_大咖分享办公秘籍:Word和Excel换行

    我们在日常办公和学习中 对文档进行整理和粘贴数据的过程中 经常会用到的就是换行功能了 那么怎么实现自动换行呢 下面 就由非凡学院商务办公培训专业的付老师 和大家分享一下在Word和Excel中简单的实现过程 赶紧来看看和学习一下吧 1 硬回
  • 虚拟机网络配置、防火墙、克隆

    一 修改虚拟机的网络配置 在虚拟机开启的情况下配置 选中虚拟机hadoop100 编辑 虚拟网络编辑器 更改设置 NAT模式 NAT设置 网关改为192 168 1 2 连点两次确定 二 修改windows的网络配置 1 点击电脑右下角的网
  • 华为OD机试 - 靠谱的车 - 逻辑分析(Java 2023 B卷 100分)

    目录 专栏导读 一 题目描述 二 输入描述 三 输出描述 四 解题思路 五 Java算法源码 六 效果展示 1 输入 2 输出 3 说明 华为OD机试 2023B卷题库疯狂收录中 刷题点这里 专栏导读 本专栏收录于 华为OD机试 JAVA
  • DBeaver报Public Key Retrieval is not allowed 错误

    解决方法 1 选择报错的链接 按F4 出现编辑链接弹窗 切换到驱动属性标签页 2 修改allowPublicKeyRetrieval属性的值为TRUE 3 重启DBeaber
  • Git+TortoiseGit详细安装教程(HTTP方式)

    Git是核心 TortoiseGit是个外壳 git需要使用命令行 可以简单理解为TortoiseGit是git的GUI展现形式 使用TortoiseGit操作 就不用记住git命令了 Git可以使用HTTP和SSH方式 HTTP方式配置更
  • 素数筛

    素数又称质数 是指除了1和它本身 再也没有其他的因子 合数是除了1和它本身以外 还有其他的因子 1既不是素数也不是合数 此题为洛谷上的一道题 以此题为例题 讲解素数筛 埃氏筛法 时间复杂度O nlogn 得分 80 include
  • html比较字符数字大小,JavaScript怎么比较字符串?

    javascript怎么比较字符串 下面本篇文章就来给大家介绍使用javascript比较字符串的几种方法 希望对大家有所帮助 一 大于 gt 小于 javascript字符串在进行大于 小于 比较时 会根据第一个不同的字符的ascii值码
  • 预训练模型、迁移学习

    预训练模型把迁移学习很好地用起来了 让我们感到眼前一亮 这和小孩子读书一样 一开始语文 数学 化学都学 读书 网上游戏等 在脑子里积攒了很多 当他学习计算机时 实际上把他以前学到的所有知识都带进去了 如果他以前没上过中学 没上过小学 突然学
  • linux 查看文件个数

    要查看 Linux 系统中某个目录下的文件数量 可以使用 ls 命令结合管道和 wc 命令来完成 具体来说 可以使用 ls 命令的 l 参数来列出目录中的文件和子目录的详细信息 然后将输出结果通过管道 传递给 wc 命令 使用 l 参数来统
  • sort()函数与qsort()函数及其头文件

    sort 函数是C 中的排序函数其头文件为 include
  • vscode路径别名文件跳转解决办法

    第一步 下载 1 在jsconfig json中配置 compilerOptions target es5 module esnext baseUrl moduleResolution node paths src lib esnext d
  • Code Llama: Open Foundation Models for Code

    Paper name Code Llama Open Foundation Models for Code Paper Reading Note Paper URL https ai meta com research publicatio
  • SpringBoot + mybatis + mysql8.0.11 构建项目

    前几天在阐释使用mysql8 0 11 刚解决第三方工具链接没数据库的问题 然后兴高采烈的去搭建项目 进行使用 然后又是一种一种的问题冒出 具体出现的问题 以及相关的解决方式如下 主要是在建立链接的时候一些配置参数的问题 本人使用的是Int
  • 都2023年了,为什么大家还都在吹捧 Python?

    2023 年 Python 还可学吗 答案当然是可 近些年间 Python 的火热有目共睹 作为一种功能强大的高级编程语言 在 2018 年的时候它的流行程度就得到了大幅提高 图源 Stack Overflow 网站编程语言浏览量统计数字
  • PyTorch教程—B站刘二大人笔记

    PyTorch教程 B站刘二大人笔记 PyTorch代码笔记 1 linear py 2 gradient py 3 back py 4 pytorch py 5 logistic py 6 multiple py 7 data py 8
  • C++链表的建立,排序,删除

    描述 使用随机数建立链表节点 节点的结构很简单 就是一个整形数 随机数 和一个指针 有三个函数 第一个函数建立链表 第二个函数对链表进行排序 第三个函数将链表中所有有重复数字的节点删除 只留只出现过一次的节点 输入 链表的节点数 输出 1
  • Beyond Joint Demosaicking and Denoising: An Image Processing Pipeline for a Pixel-bin Image Sensor

    Abstract 像素合并被认为是解决智能手机相机硬件限制的最突出的解决方案之一 尽管有许多优点 但这种图像传感器必须采用容易产生伪影的非拜耳滤色器阵列 CFA 才能实现合并功能 相反 明确地使用此类 CFA 模式执行去马赛克和去噪等基本图
  • stm32项目平衡车详解(stm32F407)下

    stm32项目平衡车详解 stm32F407 下 本文章学习借鉴于创客学院团队 以表感谢 教学视频 B站学习地址 HC SRO4 超声波测距避障功能开发 TSL1401 CCD摄像头实现小车巡线功能 小车通信功能分析及ESP32模块介绍 o