stm32项目平衡车详解(stm32F407)下
HC-SRO4 超声波测距避障功能开发
TSL1401 CCD摄像头实现小车巡线功能
文章目录
- stm32项目平衡车详解(stm32F407)下
- 前言
- 一、HC-SRO4 超声波测距避障功能开发
- HC-SRO4超声波测距模块?
- 超声波测距避障功能开发
- 避障模式开发
- 二、TSL1401 CCD摄像头实现小车巡线功能
- TSL1401 CCD摄像头?
- 巡线小车功能开发(编写程序)
- TSL1401 示例代码如下:
- 项目工程开始
- 三、遥控小车
- 蓝牙控制
- esp32 网络控制
前言
前面我们已经实现了平衡小车的直立环,平衡环,转向环,下面我们将实现小车平衡小车蔽障与巡线功能的开发。如下如所示:
一、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,开启中断
esp32 使用例程模板
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*/
上一节,stm32项目平衡车详解(stm32F407)上