使用STM32 再实现循迹/跟随/摇头避障小车

循迹小车

硬件介绍和接线

TCRT5000

使用方法和原理见89C52时期的介绍。

循迹小车需要使用两个TCRT5000,左侧的DO接到PB3右侧的DO接到PB4

CubeMX

1. 在上节的基础上进行修改 + 配置两个传感器的GPIO

2. 惯例配置更新代码

Keil

注意,如果要使用PWM调速就必须全部是PWM调速,因为一旦使用PWM调速之后又使用其他motor.c中的前后左右,就会重置PB1和PB10的值,而如果要使用调速,PB1和PB10就必须一直为HIGH,使用PWM调速的优点是转弯平滑,缺点是电机很容易因为PWM波的有效电平持续时间过低而转不动。

如果不想用PWM调速,不仅不能调用PWM调速的函数,在main中也记得将PWM使能关闭,在Cube中要把PA1和PA2改回GPIO,不使用PWM调速的优点是电机动力充足,缺点是转弯不太平滑。

按照代码封装的方法,封装route_sensor.croute_sensor.h:

route_sensor.c:

#include "route_sensor.h"
#include "motor.h"
#include "gpio.h"void route_sensor_mode()
{if((HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_3) == GPIO_PIN_RESET) && (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_4) == GPIO_PIN_RESET)){ //此时左右都没有检测到黑线,说明在轨迹上,直走, 两个灯都亮move_forward();//speed_left(180); //speed就是CCRx的值,理论在0到199之间,实际最好大于130//speed_right(180);}if((HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_3) == GPIO_PIN_SET) && (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_4) == GPIO_PIN_RESET)){ //此时左模块检测到黑线,说明车身偏右即将离开轨道,此时需要左转修正,只有右灯亮move_leftturn();//speed_left(160); //speed就是CCRx的值,理论在0到199之间,实际最好大于130//speed_right(199);}if((HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_3) == GPIO_PIN_RESET) && (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_4) == GPIO_PIN_SET)){ //此时右模块检测到黑线,说明车身偏左即将离开轨道,此时需要右转修正,只有左灯亮move_rightturn();//speed_left(199); //speed就是CCRx的值,理论在0到199之间,实际最好大于130//speed_right(160);}if((HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_3) == GPIO_PIN_SET) && (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_4) == GPIO_PIN_SET)){ //此时左右都检测到黑线,说明不在轨迹上了,停止,两个灯都灭move_stop();//speed_left(0); //speed就是CCRx的值,理论在0到199之间,实际最好大于130//speed_right(0);}}

route_sensor.h:

#ifndef __route_sensor_H__ // "XXXX"就是h文件的名字
#define __route_sensor_H__void route_sensor_mode(void);#endif 

main.c:

#include "motor.h"
#include "route_sensor.h"int main(void)
{//HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1); //打开Timer2的1号Channel, 1号通道对应 左轮//HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_2); //打开Timer2的2号Channel, 2号通道对应 右轮while (1){route_sensor_mode();}
}

实现效果

实现效果和89C52时一样,所以不重复展示了。

循迹小车

硬件介绍和接线

红外避障模块

使用方法和原理见89C52时期的介绍。

跟随小车需要使用两个红外避障模块,左侧的DO接到PB5右侧的DO接到PB6

CubeMX

1. 在上节的基础上进行修改 + 配置两个传感器的GPIO

2. 惯例配置更新代码

Keil

按照代码封装的方法,封装route_follow.croute_follow.h:

route_follow.c:

#include "route_follow.h"
#include "motor.h"
#include "gpio.h"void route_follow_mode()
{if((HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_5) == GPIO_PIN_RESET) && (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_6) == GPIO_PIN_RESET)){ //此时左右都没有检测到黑线,说明在轨迹上,直走, 两个灯都亮move_forward();}if((HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_5) == GPIO_PIN_SET) && (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_6) == GPIO_PIN_RESET)){ //此时左模块检测到黑线,说明车身偏右即将离开轨道,此时需要左转修正,只有右灯亮move_rightturn();}if((HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_5) == GPIO_PIN_RESET) && (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_6) == GPIO_PIN_SET)){ //此时右模块检测到黑线,说明车身偏左即将离开轨道,此时需要右转修正,只有左灯亮move_leftturn();}if((HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_5) == GPIO_PIN_SET) && (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_6) == GPIO_PIN_SET)){ //此时左右都检测到黑线,说明不在轨迹上了,停止,两个灯都灭move_stop();}}

route_follow.h:

#ifndef __route_follow_H__ // "XXXX"就是h文件的名字
#define __route_follow_H__void route_follow_mode(void);#endif 

main.c:

#include "motor.h"
#include "route_follow.h"int main(void)
{//HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1); //打开Timer2的1号Channel, 1号通道对应 左轮//HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_2); //打开Timer2的2号Channel, 2号通道对应 右轮while (1){route_follow_mode();}
}

实现效果

实现效果和89C52时一样,所以不重复展示了。

摇头避障小车

硬件介绍和接线

SG90舵机(实现摇头)

HC-SR04超声波测距模块(实现避障) 

由于驱动舵机使用的PWM也是周期为20ms的,所以可以直接用TIM1的CH3,对应PA2 

HC-SR04的Trig接PA5;Echo接PA4

CubeMX

1. 在上节的基础上进行修改 + 配置Trig和Echo的GPIO:

 

2. 打开TIM2的CH3的PWM,由于PSC和ARR已经设置过了所以不用设置,有效电平为HIGH也不用设置:

3. 打开TIM3,用作HC-SR04的计时器,并且只用来作为计数功能,并软件控制何时停止计数,因此只需要设置TIM2的PSC而不需要设置ARR。

神奇的事情又发生了...在前几个项目里,同样是HC-SR04的初始化,使用的是微秒级的延时,但是在这个项目里,又变回了毫秒级的延时???唯一的区别是,上个项目使用的是TIM2,此处是TIM3...

4. 惯例配置更新代码

Keil

按照代码封装的方法,封装SG90.c, SG90.h, HC_SR04.c, HC_SR04.h:

SG90.c:

#include "SG90.h"
#include "tim.h"int angle;void head_right()
{angle = 0;__HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_3, 5); //0度
}void head_middle()
{angle = 1;__HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_3, 15); //90度
}void head_left()
{angle = 2;__HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_3, 25); //180度
}

SG90.h:

#ifndef __SG90_H__ // "XXXX"就是h文件的名字
#define __SG90_H__void head_right(void);
void head_middle(void);
void head_left(void);#endif 

HC_SR04.c:

#include "HC_SR04.h"
#include "SG90.h"
#include "gpio.h"
#include "tim.h"
#include "motor.h"double dist_middle;
double dist_left;
double dist_right;void StartHC()
{HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET); //Trig写0HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET); //Trig写1HAL_Delay(10);HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET); //Trig写0}double get_dist()
{int cnt;double dist;StartHC();while((HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_4)) == GPIO_PIN_RESET); //等待Echo变高的一瞬间HAL_TIM_Base_Start(&htim3); //TIM3开始计时__HAL_TIM_SetCounter(&htim3,0); //将TIM3的计数器置0while((HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_4)) == GPIO_PIN_SET); //等待Echo变低的一瞬间	HAL_TIM_Base_Stop(&htim3); //TIM3停止计时cnt = __HAL_TIM_GetCounter(&htim3);//求出计了多少次,由于计数一次经过的时间是1usdist = cnt*340/2*0.000001*100;  //求出距离return dist;
}void deal_dist()
{dist_middle = get_dist();if(dist_middle > 35){ //如果距离大于35cm就可以前进move_forward();//前进}else if((dist_middle > 10) && (dist_middle < 35)){ //距离小于35cm但大于10cm时move_stop();//先停下head_left();//将测距传感器通过SG90转到左边dist_left = get_dist();HAL_Delay(300);//根据实际情况head_right();//将测距传感器通过SG90转到右边dist_right = get_dist();HAL_Delay(300);//根据实际情况if(dist_right > dist_left){ //哪边距离大往哪边转move_rightturn();HAL_Delay(300);//根据实际情况move_stop();}else{move_leftturn();HAL_Delay(300);//根据实际情况move_stop();}}else{ //当距离已经小于10cm时move_backward();//此时距离已经危险了,需要后退一下HAL_Delay(300);//根据实际情况move_stop();}
}

HC_SR04.h:

#ifndef __HC_SR04_H__ // "XXXX"就是h文件的名字
#define __HC_SR04_H__void deal_dist(void);#endif 

main.c: 

#include "SG90.h"
#include "HC_SR04.h"extern int angle;int main(void)
{	//HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1); //打开Timer2的1号Channel, 1号通道对应 左轮//HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_2); //打开Timer2的2号Channel, 2号通道对应 右轮HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_3); //打开Timer2的3号Channel, 控制SG90head_middle();while (1){if(angle != 1){ //如果不在中间的话,回到中间head_middle();HAL_Delay(300);}deal_dist();HAL_Delay(50); //重要}
}

实现效果

实现效果和89C52时一样,所以不重复展示了。

 

 

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

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

相关文章

docker-compose yml配置、常用命令

下载完docker-compose后&#xff0c;如果想使用docker-compose命令开头&#xff0c;需要创建软连接 sudo ln -s /usr/local/lib/docker/cli-plugins/docker-compose /usr/bin/docker-compose 1.docker-compose.yml文件编排 一个 docker-compose.yml 文件的顶层元素有&#xff…

flask介绍、快速使用、配置文件、路由系统

前言: Flask框架和Django框架的区别&#xff1a; Django框架&#xff1a; 大而全&#xff0c;内置的app的很多&#xff0c;第三方app也很多Flask框架&#xff1a; 小而精&#xff0c;没有过多的内置app&#xff0c;只能完成web框架的基本功能&#xff0c;很多功能都需要借助第三…

Ceph组件

Ceph组件 无论是想向云平台提供Ceph 对象存和 Ceph 块设备服务、部署Ceph 文件系统,或者是将 Ceph 用于其他目的,所有 Ceph 存储集群部署都从设置每个 Ceph 节点、网络开始。 一个Ceph 存储集群至少需要一个Ceph Monitor、Ceph Manager和 Ceph OSD (OBJECT STORAGE DAEMON对象存…

翻转单词顺序

剑指 Offer 58 - I. 翻转单词顺序 输入一个英文句子&#xff0c;翻转句子中单词的顺序&#xff0c;但单词内字符的顺序不变。为简单起见&#xff0c;标点符号和普通字母一样处理。例如输入字符串"I am a student. “&#xff0c;则输出"student. a am I”。 示例 1…

[虚幻引擎] UE里面监控每帧循环里面 C++ 函数的性能, 函数耗时,函数效率,函数执行时间

在使用C开发UE引擎&#xff0c;有时候需要监控函数的执行的执行效率&#xff0c;这个时候有两种方式可以使用。 1. 执行代码耗时时间 double ThisTime 0;{SCOPE_SECONDS_COUNTER(ThisTime);// ...// 一串代码// ...}UE_LOG(LogTemp, Log, TEXT("Stats::Broadcast %.2f&qu…

65英寸OLED透明屏的显示效果出色吗?

65英寸OLED透明屏是一种新型的显示技术&#xff0c;它采用有机发光二极管&#xff08;OLED&#xff09;作为显示元件&#xff0c;具有高亮度、高对比度、快速响应和广视角等优点。 与传统的液晶显示屏相比&#xff0c;OLED透明屏具有更高的透明度和更好的显示效果。 OLED透明屏…

【STM32】使用HAL库对ULN2003控制28BYJ-48步进电机

步进电机是将电脉冲信号转变为角位移或线位移&#xff0c;通过控制施加在电机线圈上的电脉冲顺序、频率和数量&#xff0c;可以控制步进电机的转向、速度和旋转角度。 配合以直线运动执行机构(螺纹丝杆)或齿轮箱装置&#xff0c;更可以实现更加复杂、精密的线性运动控制要求。…

PDF文件转换成word软件有哪些?分享两个文件格式转换软件

在日常办公中&#xff0c;我们经常使用各种办公软件&#xff0c;其中PDF和Word是最常见的两种格式。相较于Word文件&#xff0c;PDF文件具有更强的兼容性和安全性&#xff0c;因此我们通常会选择以PDF格式分享文件。然而&#xff0c;如果我们需要提取PDF文件中的部分内容&#…

计算机启动过程uefi+gpt方式

启动过程&#xff1a; 一、通电 按下开关&#xff0c;不用多说 二、uefi阶段 通电后&#xff0c;cpu第一条指令是执行uefi固件代码。 uefi固件代码固化在主板上的rom中。 &#xff08;一&#xff09;uefi介绍 UEFI&#xff0c;全称Unified Extensible Firmware Interface&am…

JVM运行时数据区——字符串常量池位置的调整

在JDK6及之前&#xff0c;使用永久代来实现方法区&#xff0c;字符串常量池(StringTable)是在永久代(方法区)中的&#xff0c;但是方法区的回收效率不高&#xff0c;在Full GC时才会回收。 在JDK7中&#xff0c;将字符串常量池转移到了堆中&#xff0c;分配在年轻代和老年代中。…

linux 使用nethogs命令查看各个进程使用网络的情况

1&#xff0c;使用nethogs命令查看各个进程使用网络的情况 #yum -y install nethogs 2&#xff0c;使用nethogs命令查看进程使用网络的情况 #nethogs

PHP8知识详解:PHP8的新特性

PHP 8是PHP编程语言的一个主要版本&#xff0c;在2020年11月26日发布。它引入了许多新特性和改进&#xff0c;包括以下一些主要特性&#xff1a; 1. JIT 编译器&#xff1a;PHP 8引入了名为Tracing JIT的即时&#xff08;Just-In-Time&#xff09;编译器。JIT可以将PHP脚本中频…

【C++ 进阶】继承

一.继承的定义格式 基类又叫父类&#xff0c;派生类又叫子类&#xff1b; 二.继承方式 继承方式分为三种&#xff1a; 1.public继承 2.protected继承 3.private继承 基类成员与继承方式的关系共有9种&#xff0c;见下表&#xff1a; 虽然说是有9种&#xff0c;但其实最常用的还…

Stable diffusion 和 Midjourney 怎么选?

通过这段时间的摸索&#xff0c;我将和你探讨&#xff0c;对普通人来说&#xff0c;Stable diffusion 和 Midjourney 怎么选&#xff1f;最重要的是&#xff0c;学好影视后期制作对 AI 绘画创作有哪些帮助&#xff1f;反过来&#xff0c;AI 绘画对影视后期又有哪些帮助&#xf…

28.JavaWeb-Elasticsearch

1.Elasticsearch概述 Elasticsearch 是一个分布式的全文检索引擎。采用Java语言开发&#xff0c;基于Apache协议的开源项目&#xff0c;具有实时搜索&#xff0c;稳定&#xff0c;可靠&#xff0c;快速的特点。 1.1 全文检索引擎 分为通用搜索引擎&#xff08;百度、谷歌&…

【深度学习】【Image Inpainting】Generative Image Inpainting with Contextual Attention

Generative Image Inpainting with Contextual Attention DeepFillv1 (CVPR’2018) 论文&#xff1a;https://arxiv.org/abs/1801.07892 论文代码&#xff1a;https://github.com/JiahuiYu/generative_inpainting 论文摘录 文章目录 效果一览摘要介绍论文贡献相关工作Image…

【Java SE】类和对象

目录 【1】面向对象的初步认识 【1.1】什么是面向对象 【1.2】面向对象与面向过程 【2】类定义和使用 【2.1】简单认识类 【2.2】类的定义格式 【2.3】练习 【2.3.1】定义一个狗类 【2.3.2】定义一个学生类 【3】类的实例化 【3.1】什么是实例化 【3.2】类和对象的…

【Java基础学习打卡18】运算符(上)

目录 前言一、运算符和表达式1.运算符2.表达式 二、算术运算符1.加法运算符2.减法运算符3.乘法运算符4.除法运算符5.取余运算符6.表达式类型自动提升 总结 前言 本文主要介绍运算符和表达式&#xff0c;及运算符中的算术运算符。在 Java 编程中&#xff0c;运算符起着非常重要…

Java 使用 Google Guava 实现接口限流

一、引入依赖 <dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>30.0-jre</version> </dependency>二、自定义注解及限流拦截器 自定义注解&#xff1a;Limiter package com.haita…

安装requests模块及其他依赖库的完美解决办法

业务场景 导入requests库时报错&#xff0c;单独离线下载安装requests&#xff0c;发现仍然报错&#xff0c;问题在于requests库有其他依赖库。 WARNING: Retrying (Retry(total1, connectNone, readNone, redirectNone, statusNone)) after connection broken by NewConnect…