第18章-综合以上功能 基于stm32的智能小车(远程控制、避障、循迹) 基于stm32f103c8t6/HAL库/CubeMX/超详细,包含代码讲解和原理图

这个是全网最详细的STM32项目教学视频。
第一篇在这里:
视频在这里

STM32智能小车V3-STM32入门教程-openmv与STM32循迹小车-stm32f103c8t6-电赛 嵌入式学习 PID控制算法 编码器电机 跟随

第18章-综合以上功能

18-按键和app按钮切换功能

根据上面介绍,我们的模式可以有:

**OLED显示模式: 速度、里程、电压、超声波数据、MPU6050俯仰角、横滚角、航向角 数据显示在OLED上和通过串口发送蓝牙APP **

PID循迹模式:红外对管PID循迹

手机遥控普通运动模式:遥控前、后、左、右加速运动

超声波避障模式

PID跟随模式:超声波PID定距离跟随

手机遥控角度闭环模式:MPU6050角度PID控制

可以设置标志位通过按键改变标志位,以实现功能切换。

定义一个全局变量,

uint8_t g_ucMode = 0; 
//小车运动模式标志位 0:显示功能、1:PID循迹模式、2:手机遥控普通运动模式、3.超声波避障模式、4:PID跟随模式、5:遥控角度闭环

在这里插入图片描述

uint8_t g_ucMode = 0; //小车运动模式标志位

在gpio.h声明一下

在这里插入图片描述

extern uint8_t g_ucMode ; //小车运动模式标志位

按键中断回调函数里面补充按下按键后的处理

先不进行消抖,如果后面KEY1 KEY2效果不好再消抖
在这里插入图片描述

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{if(GPIO_Pin == KEY1_Pin) //判断一下那个引脚触发中断{//这里编写触发中断后要执行的程序if(g_ucMode == 5) g_ucMode = 1;//g_ucMode模式是0 1 2 3 4 5 else{g_ucMode+=1;}HAL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin);}if(GPIO_Pin == KEY2_Pin) //判断一下那个引脚触发中断{//这里编写触发中断后要执行的程序g_ucMode=0;HAL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin);}
}

然后主函数显示当前处于的模式

然后判断当前模式 执行不同代码

方法:一个功能一个功能的添加代码,添加好一个调试测试一下,然后再添加下一个

下面这个就是我们主函数的代码。

	sprintf((char *)OledString," g_ucMode:%d",g_ucMode);//显示g_ucMode 当前模式OLED_ShowString(0,6,OledString,12);	//显示在OLED上sprintf((char *)Usart3String," g_ucMode:%d",g_ucMode);//蓝牙APP显示HAL_UART_Transmit(&huart3,( uint8_t *)Usart3String,strlen(( const  char  *)Usart3String),50);//阻塞式发送通过串口三输出字符 strlen:计算字符串大小if(g_ucMode == 0){//0LED显示功能sprintf((char*)OledString, "V1:%.2fV2:%.2f", Motor1Speed,Motor2Speed);//显示速度OLED_ShowString(0,0,OledString,12);//这个是oled驱动里面的,是显示位置的一个函数,sprintf((char*)OledString, "Mileage:%.2f", Mileage);//显示里程OLED_ShowString(0,1,OledString,12);//这个是oled驱动里面的,是显示位置的一个函数,sprintf((char*)OledString, "U:%.2fV", adcGetBatteryVoltage());//显示电池电压OLED_ShowString(0,2,OledString,12);//这个是oled驱动里面的,是显示位置的一个函数,sprintf((char *)OledString,"HC_SR04:%.2fcm\r\n",HC_SR04_Read());//显示超声波数据OLED_ShowString(0,3,OledString,12);//这个是oled驱动里面的,是显示位置的一个函数,sprintf((char *)OledString,"p:%.2f r:%.2f \r\n",pitch,roll);//显示6050数据 俯仰角 横滚角OLED_ShowString(0,4,OledString,12);//这个是oled驱动里面的,是显示位置的一个函数,sprintf((char *)OledString,"y:%.2f  \r\n",yaw);//显示6050数据  航向角OLED_ShowString(0,5,OledString,12);//这个是oled驱动里面的,是显示位置的一个函数,//蓝牙APP显示sprintf((char*)Usart3String, "V1:%.2fV2:%.2f", Motor1Speed,Motor2Speed);//显示速度HAL_UART_Transmit(&huart3,( uint8_t *)Usart3String,strlen(( const  char  *)Usart3String),50);//阻塞式发送通过串口三输出字符 strlen:计算字符串大小//阻塞方式发送可以保证数据发送完毕,中断发送不一定可以保证数据已经发送完毕才启动下一次发送sprintf((char*)Usart3String, "Mileage:%.2f", Mileage);//显示里程HAL_UART_Transmit(&huart3,( uint8_t *)Usart3String,strlen(( const  char  *)Usart3String),50);//阻塞式发送通过串口三输出字符 strlen:计算字符串大小sprintf((char*)Usart3String, "U:%.2fV", adcGetBatteryVoltage());//显示电池电压HAL_UART_Transmit(&huart3,( uint8_t *)Usart3String,strlen(( const  char  *)Usart3String),50);//阻塞式发送通过串口三输出字符 strlen:计算字符串大小sprintf((char *)Usart3String,"HC_SR04:%.2fcm\r\n",HC_SR04_Read());//显示超声波数据HAL_UART_Transmit(&huart3,( uint8_t *)Usart3String,strlen(( const  char  *)Usart3String),50);//阻塞式发送通过串口三输出字符 strlen:计算字符串大小sprintf((char *)Usart3String,"p:%.2f r:%.2f \r\n",pitch,roll);//显示6050数据 俯仰角 横滚角HAL_UART_Transmit(&huart3,( uint8_t *)Usart3String,strlen(( const  char  *)Usart3String),50);//阻塞式发送通过串口三输出字符 strlen:计算字符串大小sprintf((char *)Usart3String,"y:%.2f  \r\n",yaw);//显示6050数据  航向角HAL_UART_Transmit(&huart3,( uint8_t *)Usart3String,strlen(( const  char  *)Usart3String),50);//阻塞式发送通过串口三输出字符 strlen:计算字符串大小//获得6050数据while(mpu_dmp_get_data(&pitch,&roll,&yaw)!=0){}  //这个可以解决经常读不出数据的问题//显示模式电机停转motorPidSetSpeed(0,0);}if(g_ucMode == 1){///****    红外PID循迹功能******************/g_ucaHW_Read[0] = READ_HW_OUT_1;//读取红外对管状态、这样相比于写在if里面更高效g_ucaHW_Read[1] = READ_HW_OUT_2;g_ucaHW_Read[2] = READ_HW_OUT_3;g_ucaHW_Read[3] = READ_HW_OUT_4;if(g_ucaHW_Read[0] == 0&&g_ucaHW_Read[1] == 0&&g_ucaHW_Read[2] == 0&&g_ucaHW_Read[3] == 0 ){
//		printf("应该前进\r\n");//注释掉更加高效,减少无必要程序执行g_cThisState = 0;//前进}else if(g_ucaHW_Read[0] == 0&&g_ucaHW_Read[1] == 1&&g_ucaHW_Read[2] == 0&&g_ucaHW_Read[3] == 0 )//使用else if更加合理高效{
//		printf("应该右转\r\n");g_cThisState = -1;//应该右转}else if(g_ucaHW_Read[0] == 1&&g_ucaHW_Read[1] == 0&&g_ucaHW_Read[2] == 0&&g_ucaHW_Read[3] == 0 ){
//		printf("快速右转\r\n");g_cThisState = -2;//快速右转}else if(g_ucaHW_Read[0] == 1&&g_ucaHW_Read[1] == 1&&g_ucaHW_Read[2] == 0&&g_ucaHW_Read[3] == 0){
//		printf("快速右转\r\n");g_cThisState = -3;//快速右转}else if(g_ucaHW_Read[0] == 0&&g_ucaHW_Read[1] == 0&&g_ucaHW_Read[2] == 1&&g_ucaHW_Read[3] == 0 ){
//		printf("应该左转\r\n");g_cThisState = 1;//应该左转	}else if(g_ucaHW_Read[0] == 0&&g_ucaHW_Read[1] == 0&&g_ucaHW_Read[2] == 0&&g_ucaHW_Read[3] == 1 ){
//		printf("快速左转\r\n");g_cThisState = 2;//快速左转}else if(g_ucaHW_Read[0] == 0&&g_ucaHW_Read[1] == 0&&g_ucaHW_Read[2] == 1&&g_ucaHW_Read[3] == 1){
//	    printf("快速左转\r\n");g_cThisState = 3;//快速左转}g_fHW_PID_Out = PID_realize(&pidHW_Tracking,g_cThisState);//PID计算输出目标速度 这个速度,会和基础速度加减g_fHW_PID_Out1 = 3 + g_fHW_PID_Out;//电机1速度=基础速度+循迹PID输出速度g_fHW_PID_Out2 = 3 - g_fHW_PID_Out;//电机1速度=基础速度-循迹PID输出速度if(g_fHW_PID_Out1 >5) g_fHW_PID_Out1 =5;//进行限幅 限幅速度在0-5之间if(g_fHW_PID_Out1 <0) g_fHW_PID_Out1 =0;if(g_fHW_PID_Out2 >5) g_fHW_PID_Out2 =5;if(g_fHW_PID_Out2 <0) g_fHW_PID_Out2 =0;if(g_cThisState != g_cLastState)//如何这次状态不等于上次状态、就进行改变目标速度和控制电机、在定时器中依旧定时控制电机{motorPidSetSpeed(g_fHW_PID_Out1,g_fHW_PID_Out2);//通过计算的速度控制电机}g_cLastState = g_cThisState;//保存上次红外对管状态	}if(g_ucMode == 2){//***************遥控模式***********************////遥控模式的控制在串口三的中断里面}if(g_ucMode == 3){//******超声波避障模式*********************//
避障逻辑if(HC_SR04_Read() > 25)//前方无障碍物{motorPidSetSpeed(1,1);//前运动HAL_Delay(100);}else{	//前方有障碍物motorPidSetSpeed(-1,1);//右边运动 原地	HAL_Delay(500);if(HC_SR04_Read() > 25)//右边无障碍物{motorPidSetSpeed(1,1);//前运动HAL_Delay(100);}else{//右边有障碍物motorPidSetSpeed(1,-1);//左边运动 原地HAL_Delay(1000);if(HC_SR04_Read() >25)//左边无障碍物{motorPidSetSpeed(1,1);//前运动HAL_Delay(100);}else{motorPidSetSpeed(-1,-1);//后运动HAL_Delay(1000);motorPidSetSpeed(-1,1);//右边运动HAL_Delay(50);}}}}if(g_ucMode == 4){//**********PID跟随功能***********//g_fHC_SR04_Read=HC_SR04_Read();//读取前方障碍物距离if(g_fHC_SR04_Read < 60){  //如果前60cm 有东西就启动跟随g_fFollow_PID_Out = PID_realize(&pidFollow,g_fHC_SR04_Read);//PID计算输出目标速度 这个速度,会和基础速度加减if(g_fFollow_PID_Out > 6) g_fFollow_PID_Out = 6;//对输出速度限幅if(g_fFollow_PID_Out < -6) g_fFollow_PID_Out = -6;motorPidSetSpeed(g_fFollow_PID_Out,g_fFollow_PID_Out);//速度作用与电机上}else motorPidSetSpeed(0,0);//如果前面60cm 没有东西就停止HAL_Delay(10);//读取超声波传感器不能过快}if(g_ucMode == 5){//*************MPU6050航向角 PID转向控制*****************//sprintf((char *)Usart3String,"pitch:%.2f roll:%.2f yaw:%.2f\r\n",pitch,roll,yaw);//显示6050数据 俯仰角 横滚角 航向角HAL_UART_Transmit(&huart3,( uint8_t *)Usart3String,strlen(( const  char  *)Usart3String),0xFFFF);//通过串口三输出字符 strlen:计算字符串大小	//mpu_dmp_get_data(&pitch,&roll,&yaw);//返回值:0,DMP成功解出欧拉角while(mpu_dmp_get_data(&pitch,&roll,&yaw)!=0){}  //这个可以解决经常读不出数据的问题g_fMPU6050YawMovePidOut = PID_realize(&pidMPU6050YawMovement,yaw);//PID计算输出目标速度 这个速度,会和基础速度加减g_fMPU6050YawMovePidOut1 = 1.5 + g_fMPU6050YawMovePidOut;//基础速度加减PID输出速度g_fMPU6050YawMovePidOut2 = 1.5 - g_fMPU6050YawMovePidOut;if(g_fMPU6050YawMovePidOut1 >3.5) g_fMPU6050YawMovePidOut1 =3.5;//进行限幅if(g_fMPU6050YawMovePidOut1 <0) g_fMPU6050YawMovePidOut1 =0;if(g_fMPU6050YawMovePidOut2 >3.5) g_fMPU6050YawMovePidOut2 =3.5;if(g_fMPU6050YawMovePidOut2 <0) g_fMPU6050YawMovePidOut2 =0;motorPidSetSpeed(g_fMPU6050YawMovePidOut1,g_fMPU6050YawMovePidOut2);}

可以测试上面的代码 然后没有问题后,我们添加一个通过蓝牙APP按钮切换模式代码
在这里插入图片描述

		if(g_ucUsart3ReceiveData == 'J') //改变模式{if(g_ucMode == 5) g_ucMode = 1;//g_ucMode模式是0 1 2 3 4 5 else{g_ucMode+=1;}}if(g_ucUsart3ReceiveData == 'K') g_ucMode=0;//设置为显示模式

然后对应APP也要添加 按钮设置
在这里插入图片描述
我们
按键没有消抖效果不好,我们消抖一下
在这里插入图片描述
我们增加了 HAL延时和再次判断电平

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{if(GPIO_Pin == KEY1_Pin) //判断一下那个引脚触发中断{HAL_Delay(10);//延时消抖 主要if(HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin) == GPIO_PIN_SET)//判断KEY1引脚仍为高电平{//这里编写触发中断后要执行的程序if(g_ucMode == 5) g_ucMode = 1;//g_ucMode模式是0 1 2 3 4 5 else{g_ucMode+=1;}HAL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin);}}if(GPIO_Pin == KEY2_Pin) //判断一下那个引脚触发中断{HAL_Delay(10);//延时消抖if(HAL_GPIO_ReadPin(KEY2_GPIO_Port, KEY2_Pin) == GPIO_PIN_RESET)//判断KEY2引脚仍为低电平{//这里编写触发中断后要执行的程序g_ucMode=0;HAL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin);}}
}

但是测试不能执行中断,程序异常卡死了

原因是HAL_Delay使用的是sysTick 中断优先级在软件初始化是默认最低的,比外部中断优先级低,所以HAL_Delay不能在外部中断服务函数中调用。

所以我们可以通过提高sysTick 中断的优先级,提高的比HAL_Delay高。
在这里插入图片描述
然后我们提高至 如下图
在这里插入图片描述
然后编译烧录测试按键是否更加稳定。
下面的章节我们讲解视觉,RTOS系统,电磁循迹等功能

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/17179.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

城市空气质量数据爬取分析可视化

城市空气质量数据爬取分析可视化 一、效果展示二、完整代码2.1 数据爬取代码2.2 数据分析代码一、效果展示 先来看一下数据情况以及可视化效果,本项目使用了pyecharts绘制了日历图、雷达图、折线图、柱状图、饼图和平行坐标系。完整代码附后: 数据如下: 日历图: 饼图: …

Nose,一款多功能灵活测试的Pythonl库

Nose库概述 Nose是一个用于Python单元测试的第三方库,旨在简化和扩展Python自带的unittest框架.它提供了更多功能和灵活性,使得编写和运行单元测试变得更加方便. 安装与使用 #首先安装Nose库:pip install nose使用 nose 运行单元测试的简单示例&#xff1a; 运行测试: imp…

Go源码--sync库(1)

简介 这篇主要介绍 sync.Once、sync.WaitGroup和sync.Mutex sync.Once once 顾名思义 只执行一次 废话不说 我们看源码 英文介绍直接略过了 感兴趣的建议读一读 获益匪浅 其结构体如下 Once 是一个严格只执行一次的object type Once struct {// 建议看下源码的注解&#xf…

【找出缺失的观测数据】python

思路&#xff1a; 主要在于分配剩余的部分分配问题 代码&#xff1a; class Solution:def missingRolls(self, rolls: List[int], mean: int, n: int) -> List[int]:m len(rolls)total_sum (n m) * meantoset total_sum - sum(rolls)# 检查 toset 是否在可能的范围内i…

堆和栈的特点以及在golang中的应用

堆和栈的特点&#xff1f;在golang中有哪些应用&#xff1f; 堆&#xff08;Heap&#xff09;和栈&#xff08;Stack&#xff09;是计算机内存管理中常见的两种数据结构&#xff0c;它们在内存分配和管理方面有着不同的特点&#xff1a; 堆&#xff08;Heap&#xff09;&#x…

亚马逊高效广告打法及数据优化,亚马逊高阶广告打法课

课程下载&#xff1a;https://download.csdn.net/download/m0_66047725/89342733 更多资源下载&#xff1a;关注我。 课程内容&#xff1a; 001.1-亚马逊的广告漏斗和A9算法的升级变化.mp4 002.2-流量入口解析和广告的曝光机制.mp4 003.3-标签理论 .mp4 004.4-不同广告类…

影响所有股票、债券和ETF交易!一文看懂美国“T+1”结算新规

T1对投资者有何好处&#xff1f;有哪些风险&#xff1f;T1已经到来&#xff0c;T0还远吗&#xff1f; 美股将在本周迎来历史性时刻。 从当地时间5月28日开始&#xff0c;美股交易结算周期将由T2缩短至T1&#xff0c;即投资者当天卖出的股票&#xff0c;在交易后一个工作日就能…

蓝牙模块唤醒原理是怎样的?

随着科技的发展&#xff0c;蓝牙技术已经广泛应用于各种设备&#xff0c;如智能手机、平板电脑、智能手表等。蓝牙模块作为一种重要的通信手段&#xff0c;为我们的生活带来了极大的便利。然而&#xff0c;蓝牙模块并不仅仅是用于传输数据的工具&#xff0c;它还具有一项独特的…

eNSP学习——OSPF被动接口配置

目录 相关命令 1、配置接口IP地址 2、配置OSPF进程号、区域号以及网段宣告 3、配置被动接口 4、查看OSPF的邻居关系状态以及路由条目 原理概述 实验目的 实验步骤 实验内容 实验编址 具体步骤 1、基本配置 测试结果(仅提供一个) 2、搭建OSPF网络 3、配置被动接口…

LangChain之链的认识

Chain链 概述 为开发更复杂的应用程序&#xff0c;需要使用Chain来链接LangChain中的各个组件和功能&#xff0c;包括模型之间的链接以及模型与其他组件之间的链接。 链在内部把一系列的功能进行封装&#xff0c;而链的外部则又可以组合串联。 链其实可以被视为LangChain中的一…

unity制作app(11)--dropdown统一字体

下拉栏统一字体只能在执行的时候&#xff0c;而且要深入到content的最下层 全改以后 这样是无法保存的&#xff0c;但此时已经具备了找content的思维&#xff0c;在非play状态下做如下修改 其他下拉栏照改就可以了。

分成sum接近的2个集合,返回相对小的sum

题目描述&#xff1a;给定一个正数数组arr&#xff0c;请把arr中所有的数分成两个集合&#xff0c;尽量让两个集合的累加和接近&#xff0c;返回最接近的情况下&#xff0c;较小集合的累加和sum。 way&#xff1a;选还是不选 //arr[index...]可以自由选择,返回累加和尽量接近…

go 创建map

创建 map的集中方式: m : make(map[string]int) // 指定 map 长度 m : make(map[string]int, 8)m : map[string]int{}// m 为 nil&#xff0c;不能向其添加元素&#xff0c;会直接panic var m map[string]int底层调用的是 makemap 函数&#xff0c;主要做的工作就是初始化 hma…

【CTF Web】CTFShow web2 Writeup(SQL注入+PHP+UNION注入)

web2 1 管理员赶紧修补了漏洞&#xff0c;这下应该没问题了吧&#xff1f; 解法 注意到&#xff1a; <!-- flag in id 1000 -->但是 or 被拦截了。 if(preg_match("/or|\/i",$id)){die("id error");}使用UNION注入&#xff1a; ?id1 union sele…

【Linux-RTC】

Linux-RTC ■ rtc_device 结构体■ RTC 时间查看与设置■ 1、时间 RTC 查看■ 2、设置 RTC 时间 ■ rtc_device 结构体 Linux 内核将 RTC 设备抽象为 rtc_device 结构体 rtc_device 结构体&#xff0c;此结构体定义在 include/linux/rtc.h 文件中 ■ RTC 时间查看与设置 ■ 1…

CSS绘制圆弧

css绘制如图的圆弧&#xff1a; 这种矩形弧形的效果中&#xff0c;弧形的效果一般是由一条曲线拉伸出来的&#xff0c;这条曲线往往是属于一个椭圆的&#xff0c;所以可以绘制一个椭圆&#xff0c;截取部分可视区域实现效果。 <style> .wrapper{width: 400px;height: 60…

PHP如何实现实时计算使用者消耗服务器资源费用?

最近几天遇到一个客户,提出一个很有意思的东西!当然客户的项目方案这里不方便说,这里就假定客户的项目是腾讯云?哈哈哈哈哈 以前客户的收费方案是按月、按季度、按年收费,现在半路杀出了很多程咬金,导致之前的收费方案有点儿贵,没啥性价比,那就搞一个看起来很“便宜”…

完全背包洛谷题单

[USACO08NOV] Buying Hay S 题解&#xff1a;这题看到每个都可以卖出无限多个干草包&#xff0c;就应该想到完全背包&#xff0c;但又不同于普通的完全背包&#xff0c;普通的完全背包是让你通过对应的背包求出最大的价值&#xff0c;但是在这题理解上却是知道能够达到背包容量…

工厂模式和装饰器模式在程序设计中的体现

工厂模式 Step 1: 更新基类 在基类中添加保护方法&#xff0c;这些方法可以被子类调用以实现公共的逻辑。 model.h #ifndef MODEL_H #define MODEL_Hclass Model { public:virtual ~Model() {}void init() {preInit();doInit();postInit();}void process() {preProcess();d…

【C/C++】观察者模式

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; &#x1f525;c系列专栏&#xff1a;C/C零基础到精通 &#x1f525; 给大…