第三章 Freertos智能小车遥控控制
本文基于小车APP,通过与蓝牙模块进行连接,发送特定信号给小车主控,实现对小车的模式切换、灯光控制、前进、后退、左右控制。目前还未加入电机控制,具体的电机控制效果还不能实现,但是可以进行模式切换与灯光控制。
这里先将对应的代码和手机APP进行开源,并进行效果展示。资料下载
这里对于APP的设计和Freertos不过多赘述,有兴趣的通过这篇文章进行跳转回顾前期的内容:零基础制作Freertos智能小车(教程非常简易)
1.蓝牙连接
这里采用的是蓝牙与单片机连接的方式,这里希望大家能从第一章的教程一路看过来,方便进行学习以及器件买哪种。APP已经放在文章的开头,方便大家直接下载。
大家蓝牙模块按照下面的方式进行接线,电源的话选择3.3v-5.5v的。
/*********************蓝牙模块*********************/
//USART1_TX PA9 USART1_RX PA10
蓝牙连接成功后, 可以先进行上电测试,一般蓝牙未与手机进行连接时,蓝牙模块始终处于闪烁的状态,手机与蓝牙模块连接成功后,蓝牙模块的指示灯会变成常亮的状态。以下是具体的连接过程。蓝牙的模块根据商家的不同,可能会略有不同,可能是1234或者0000
2.串口通信(stm32)
这里如果没有串口通信基础的同学建议先看下这篇文章,看完以后能帮助大家更快的了解和学习串口协议:串口通信(基于stm32)
当手机APP发送指令到下位机的时候,会有一个信息的流转,首先发送给蓝牙模块,蓝牙模块在通过串口协议将APP的内容传给stm32。stm32的串口中断将信息存储到了USART_RX_BUF中。
void USART1_IRQHandler(void) //蓝牙信号接收中断
{static uint8_t Serial_RxState = 0; //定义表示当前状态机状态的静态变量static uint8_t Serial_pRxPacket = 0; //定义表示当前接收数据位置的静态变量if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET){uint8_t Serial_RxData = USART_ReceiveData(USART1);if (Serial_RxState == 0){if (Serial_RxData == '@' && USART_RxFlag == 0) //如果数据确实是包头,并且上一个数据包已处理完毕{Serial_RxState = 1; //置下一个状态Serial_pRxPacket = 0; //数据包的位置归零}}/*当前状态为1,接收数据包数据,同时判断是否接收到了第一个包尾*/else if (Serial_RxState == 1){if (Serial_RxData == '#') //如果收到第一个包尾{Serial_RxState = 0; //状态归0USART_RX_BUF[Serial_pRxPacket] = '\0'; //将收到的字符数据包添加一个字符串结束标志USART_RxFlag = 1; //接收数据包标志位置1,成功接收一个数据包Handle_Msg(USART_RX_BUF);}else //接收到了正常的数据{USART_RX_BUF[Serial_pRxPacket] = Serial_RxData; //将数据存入数据包数组的指定位置Serial_pRxPacket ++; //数据包的位置自增}}USART_ClearITPendingBit(USART1, USART_IT_RXNE);}
}
当我们将这批的信息处理完成后,则需要将信息进行二次的处理。通过LED[0]、LED[1]控制灯光,Mode控制模式,Contral控制电机转向(开发中...)
/*
*************************************************************************
* 全局变量
*************************************************************************
*/
int Led[2]={0,0};//数组0代表红灯,数组1代表蓝灯,数值为0时关闭灯,1时开启灯光
/*//为模式切换//Mode == 0 避障//Mode == 1 遥控//Mode == 2 跟随//Mode == 3 循迹
*/
int Mode=1;
/*//控制转向//Contral == F 前进//Contral == B 后退//Contral == L 左转//Contral == R 右转//Contral == S 停止
*/
char Contral = 'S';void Handle_Msg(char *str) //当蓝牙接收完app的信息以后,统一进入此处进行处理
{if(USART_RxFlag == 1){if (strstr(str, "RED") || strstr(str, "red") || strstr(str, "BLUE") || strstr(str, "blue")){if(strstr(str, "RED"))Led[0] = 1;else if(strstr(str, "red"))Led[0] = 0;else if(strstr(str, "BLUE"))Led[1] = 1;else if(strstr(str, "blue"))Led[1] = 0;}else if(strstr(str, "G")){Mode = 2;//Oled_Show_Image(2);}else if(strstr(str, "X")){Mode = 3;//Oled_Show_Image(0);}else if(strstr(str, "D")){Mode = 0;//Oled_Show_Image(2);}else if(strstr(str, "Y")){Mode = 1;//Oled_Show_Image(1);}else if(strstr(str, "F")){Contral = 'F';}else if(strstr(str, "B")){Contral = 'B';}else if(strstr(str, "L")){Contral = 'L';}else if(strstr(str, "R")){Contral = 'R';}else if(strstr(str, "S")){Contral = 'S';}USART_RxFlag = 0;}
}
3.利用Freertos创建Red_Blue_LED_Task控制小灯任务
创建的任务过程可以先参考第一章 Freertos入门介绍
首先我们创建任务句柄
/**************************** 任务句柄 ********************************/
/* * 任务句柄是一个指针,用于指向一个任务,当任务创建好之后,它就具有了一个任务句柄* 以后我们要想操作这个任务都需要通过这个任务句柄,如果是自身的任务操作自己,那么* 这个句柄可以为NULL。*/
static TaskHandle_t AppTaskCreate_Handle = NULL;/* 创建任务句柄 */
static TaskHandle_t Red_Blue_LED_Task_Handle = NULL;/* 蓝灯任务句柄 */
下一步我们要 编辑AppTaskCreate任务,并在AppTaskCreate创建Red_Blue_LED_Task任务,并配置好任务入口函数、任务名字、任务栈大小、任务入口函数参数、任务的优先级、任务控制块指针。
/************************************************************************ @ 函数名 : AppTaskCreate* @ 功能说明: 为了方便管理,所有的任务创建函数都放在这个函数里面* @ 参数 : 无 * @ 返回值 : 无**********************************************************************/
static void AppTaskCreate(void)
{BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */taskENTER_CRITICAL(); //进入临界区/* 创建Blue_LED_Task任务 */xReturn = xTaskCreate((TaskFunction_t )Red_Blue_LED_Task, /* 任务入口函数 */(const char* )"Red_Blue_LED_Task",/* 任务名字 */(uint16_t )512, /* 任务栈大小 */(void* )NULL, /* 任务入口函数参数 */(UBaseType_t )2, /* 任务的优先级 */(TaskHandle_t* )&Red_Blue_LED_Task_Handle);/* 任务控制块指针 */if(pdPASS == xReturn)//printf("创建Blue_LED_Task任务成功!\r\n");vTaskDelete(AppTaskCreate_Handle); //删除AppTaskCreate任务taskEXIT_CRITICAL(); //退出临界区
}
接着我们要编辑Red_Blue_LED_Task任务入口函数的内容,通过数组LED来判断红灯和蓝灯的亮灭状态,并每隔100ms进入该任务进行轮询。
/*********************************************************************** @ 函数名 : Red_Blue_LED_Task* @ 功能说明: Red_Blue_LED_Task* @ 参数 : * @ 返回值 : 无********************************************************************/
static void Red_Blue_LED_Task(void* parameter)
{ while (1){if(Led[0] == 1)Red_ONelse if(Led[0] == 0)Red_OFFif(Led[1] == 1)Blue_ONelse if(Led[1] == 0)Blue_OFFvTaskDelay(100); /* 延时100个tick */}
}