学习stm32有将近两个月的时间了,于是和实验室的小伙伴一起制作了这辆小车,算是对自己前段时间所学知识的一种运用和复习
实现功能
1.超声波避障,在遇到障碍物时通过舵机带动超声波模块旋转探测下一步的行动方向。
2.红外循迹。切换为循迹功能后小车会沿着既定的黑线行动。
3.蓝牙连接。可以通过手机软件蓝牙连接小车,控制小车的移动。
4.用按钮操控以上三种功能的切换。
硬件介绍
1.我们使用的单片机是正点原子的MIniSTM32F103
2.超声波模块使用的是HC-SR04,舵机SG90,用我扎头发的皮筋儿固定在一起~
关于模块详细的参数和使用方法动动手网上都可以搜到,这里我就用自己的话讲一下
舵机:连线方面,红色接5V电源,棕色接地,橙色接IO口。IO口要输出PWM波。这款舵机的转动角度是由高电平占空比所决定的,每个占空比对应着固定的角度。下图很好的解释了这个概念
在刚刚接触的时候以为它是给多少占空比就转多少度,其实并不是如此,这个舵机能转的角度是固定的,0~180度,调整占空比是让它转到一个对应角度,给他占空比大,它就转到度数大的,给他小的占空比,它又会自己转回来。
超声波模块:VCC–5V电源,GND–接地,ECHO–接收端,Trig–控制端。首先先说一下它运作的原理。我们要向Trig端输入频率不小于10Hz的高电平,一旦超声波内部接收到了,就会自己产生一个高电平从ECHO口发射出去,直到接收到返回为止,ECHO口将一直保持高电平,接收到之后就会变为低电平。怎么样,听着是不是很耳熟,我们这段的代码完全可以魔改正点的输入捕获,来测出小车距离障碍物的距离。
3.红外模块
这个算是里面最简单的一个了,因为我们并没有用到模拟信号输出,所以可以将A0悬空,VCC–5V,GND接地,D0接IO口
它不接触黑线就两个灯都亮,如果接触到了就熄灭一个灯。
4.驱动模块TB6612
这个引脚得自己焊上去,第一次用电烙铁烧掉我一嘬头发,唉
接线说明:
VCC-接5V(我们试3.3V似乎也还行?)
VM—接12V以内的电源正级(这里需要自己外加一块电池哦)
GND-电源的负极
PWMA,PWMB:接输出PWM的IO口
A01,A02:接电机1的俩口
B01,B02:接电机2的俩口
AIN1,AIN2,BIN1,BIN2:控制轮子的状态
5.L298N的妙用
众所周知这个模块也可以作为驱动模块,但是我们这次却用它去干了别的事——引出无数的5V口。
在安装小车的时候我们发现大部分模块都需要5V电压才能带动,可是在MIni板上仅仅只有两三个5V口,远远不够使用,所以我们剥掉了几根杜邦线的皮,把他们缠在一起,给电机供给12V的电,最后这些接出来的口都拥有稳定的5v的电压,解决了我们的接口问题。
6.HC-05蓝牙模块
KEY-KEY
VCC-5V
GND–接地
RXD–TXD
TXD–RXD
STATE–STA
因为我没有做这部分的工作,全是我的小伙伴一个人完成的,大家可以移步他的博客
https://blog.csdn.net/qq_45906993/article/details/109037165
准备部分就结束了,接下来是整体的代码部分,如果想看分部同样移步上方链接看这位老哥的博客
主要代码
move.c(移动部分)
#include "delay.h"
#include "move.h"
void MOVE_Init(u16 arr,u16 psc)
{
RCC->APB1ENR |=1<<0;//TIM2时钟使能
RCC->APB2ENR |=1<<2;
RCC->APB2ENR |=1<<3;
RCC->APB2ENR |=1<<4;
GPIOA->CRL &=0XFFFF00FF;//PA2,3复用输出
GPIOA->CRL |=0X0000BB00;
GPIOB->CRH &=0XFFFFF00F;
GPIOB->CRH |=0X00000330;
GPIOC->CRL &=0X00FFFFFF;
GPIOC->CRL |=0X33000000;
TIM2->ARR=arr;//自动重装载值
TIM2->PSC=psc;//预分频系数
TIM2->CCMR2|=7<<4; //CH3 PWM2 模式//比较捕获模式配置寄存器
TIM2->CCER|=1<<8; //输出使能 //捕获/比较使能寄存器
TIM2->CCMR2|=7<<12; //CH4 PWM2 模式//比较捕获模式配置寄存器
TIM2->CCER|=1<<12; //输出使能 //捕获/比较使能寄存器
TIM2->CR1 |=0X0001;//使能定时器2
}
void left(int time)
{
//left
IN1=0;
IN2=0;
//right
IN3=0;
IN4=1;
delay_ms(time);
}
void right(int time)
{
//left
IN1=0;
IN2=1;
//right
IN3=0;
IN4=0;
delay_ms(time);
}
void back(int time)
{
//left
IN1=1;
IN2=0;
//right
IN3=1;
IN4=0;
delay_ms(time);
}
void go(int time)
{
//left
IN1=0;
IN2=1;
//right
IN3=0;
IN4=1;
delay_ms(time);
}
void stop()
{
IN1=0;
IN2=0;
IN3=0;
IN4=0;
delay_ms(10);
}
move.h
#ifndef _MOVE_H_
#define _MOVE_H_
#include "sys.h"
#define leftmotor PAout(2)
#define IN1 PBout(9)
#define IN2 PBout(10)
#define rightmotor PAout(3)
#define IN3 PCout(7)
#define IN4 PCout(6)
#define left_PWM TIM2->CCR3//占空比
#define right_PWM TIM2->CCR4//占空比
void MOVE_Init(u16 arr,u16 psc);
void left(int time);
void right(int time);
void go(int time);
void back(int time);
void stop(void);
#endif
trailing.c(红外循迹)
#include "trailing.h"
#include "move.h"
#include "usart.h"
void trailing_init()
{
RCC->APB2ENR |=1<<3;
GPIOB->CRL &=0X00FFFFFF;
GPIOB->CRL |=0X88000000;
PBin(6)=0;
PBin(7)=0;
}
void trailing()
{
trailing_init();
while(1)
{
if(returnleft==1 && returnright==0)
{
right(10);
}
else if(returnright==1 && returnleft==0)
{
left(10);
}
else if(returnleft==0 && returnright==0)
{
go(10);
}
else
{
stop();
}
}
}
trailing.h
#ifndef _TRAILING_H_
#define _TRAILING_H_
#include "sys.h"
#define returnleft PBin(6)
#define returnright PBin(7)
void trailing(void);
void trailing_init(void);
#endif
control.c(蓝牙控制)
#include "control.h"
#include "move.h"
#include "delay.h"
#include "usart.h"
u8 control_init()
{
while(1)
{
if(USART_RX_STA&0x8000)
{
printf("\r\n 您发送的消息为:\r\n");
USART1->DR=USART_RX_BUF[0];
USART_RX_STA=0;
if((USART_RX_BUF[0])==49)
return 1;
else if((USART_RX_BUF[0])==50)
return 2;
else if((USART_RX_BUF[0])==51)
return 3;
else if((USART_RX_BUF[0])==52)
return 4;
else
return 5;
}
}
}
void control()
{
u8 t=0;
while(1)
{
t=control_init();
printf("11111--%d\n",t);
switch(t)
{
case 1:go(100);break;
case 2:back(100);break;
case 3:left(100);break;
case 4:right(100);break;
case 5:stop();
default :break;
}
}
}
control.h
#ifndef _CONTROL_H_
#define _CONTROL_H_
#include "sys.h"
u8 control_init(void);
void control(void);
#endif
wave.c(舵机+超声波)
#include "wave.h"
#include "delay.h"
#include "move.h"
#include "usart.h"
/超声波测距
void TIM3_Cap_Init(u16 arr,u16 psc)
{
RCC->APB1ENR|=1<<1; //TIM2时钟使能
RCC->APB2ENR|=1<<2; //使能A时钟
GPIOA->CRL&=0XF0FFFFFF;
GPIOA->CRL|=0X08000000;
GPIOA->ODR|=0<<0;
TIM3->ARR=arr; //设定计数器自动装载值
TIM3->PSC=psc; //设定预分频数
TIM3->CCMR1|=1<<0; //CC1S=01选择输入端,IC1映射到TI1上
TIM3->CCMR1|=1<<4; //IC1F=0001 配置输入滤波器,以Fck_int采样,2个事件后有效
TIM3->CCMR1|=0<<10; //IC2PS=00 配置输入分频,不分频
TIM3->CCER|=0<<1; //CC1P=0 上升沿捕获
TIM3->CCER|=1<<0; //CC1E=1 允许捕获计数器的值到捕获寄存器中
TIM3->DIER|=1<<1; //允许捕获中断
TIM3->DIER|=1<<0; //允许更新中断
TIM3->CR1|=0x01; //使能计数器2
MY_NVIC_Init(2,0,TIM3_IRQn,2);//抢占2,子优先级0,组2
}
//捕获状态2个全局变量
//[7]:0,没有成功捕获,1,成功捕获一次
//[6]:0,还没有捕获到高电平,1,已经捕获到高电平了
//[5:0]:捕获高电平后的溢出次数
u8 TIM3CH1_CAPTURE_STA=0;//输入捕获状态
u16 TIM3CH1_CAPTURE_VAL;//输入捕获值-用来记录捕获到下降沿时,TIM2_CNT的值
//定时器2中断服务程序
void TIM3_IRQHandler(void)
{
u16 tsr;
tsr=TIM3->SR;
if((TIM3CH1_CAPTURE_STA&0X80)==0)//还没有成功捕获
{
if(tsr&0X01)//溢出
{
if(TIM3CH1_CAPTURE_STA&0X40) //已经捕获到高电平了
{
if((TIM3CH1_CAPTURE_STA&0X3F)==0X3F) //高电平太长了
{
TIM3CH1_CAPTURE_STA|=0X80; //标记成功捕获了一次
TIM3CH1_CAPTURE_VAL=0XFFFF;
}else TIM3CH1_CAPTURE_STA++;
}
}
if(tsr&0x02)//捕获1发生捕获事件
{
if(TIM3CH1_CAPTURE_STA&0X40) //捕获到一个下降沿
{
TIM3CH1_CAPTURE_STA|=0X80; //标记成功捕获到一次高电平的脉冲宽度
TIM3CH1_CAPTURE_VAL=TIM3->CCR1; //提取当前的捕获值
TIM3->CCER&=~(1<<1); //CC1P=0 设置为上升沿捕获
}else //还未开始,第一次捕获上升沿
{
TIM3CH1_CAPTURE_VAL=0;
TIM3CH1_CAPTURE_STA=0X40; //标记捕获到了上升沿
TIM3->CNT=0; //计数器清空
TIM3->CCER|=1<<1; //CC1P=1 设置为下降沿捕获
}
}
}
TIM3->SR=0; //清空中断标志位
}
void ultrasonic_Init(void){ //发射端初始化
RCC->APB2ENR |= 1<<2;
GPIOA->CRL &= 0xfff0ffff;
GPIOA->CRL |= 0X00030000;
}
//
//舵机转动/
void TIM1_PWM_Init(u16 arr,u16 psc){ //对舵机输出PWM波
RCC->APB2ENR|= 1<<11;
RCC->APB2ENR|=1<<2;
GPIOA->CRH &=0XFFFFFFF0;
GPIOA->CRH |=0X0000000B;//开启复用功能,PWM
TIM1->ARR=arr;
TIM1->PSC=psc;
TIM1->CCMR1|=7<<4;//CH1从第4位开始,设为111也就是7,PWM2模式
TIM1->CCMR1|=1<<3;
TIM1->CCER|=1<<0;//OC1输出使能
TIM1->BDTR|=1<<15;//TIM1是高级计时器,需要配置15位为1
TIM1->CR1 =0x0080;
TIM1->CR1|=0x01;
}
void RIGHTROLL_Init(void)
{
right(10);
ROLL=9500;//复位成90度
delay_ms(1000);
}
void LEFTROLL_Init(void){
int temp;
ROLL=9000;
delay_ms(1000);
temp=distance();
if(temp<=20){
RIGHTROLL_Init();
}
else{
left(10);
ROLL=9500;//复位成90度
delay_ms(1000);
}
}
void wave_init(void)
{
int temp=0;
ultrasonic_Init();
TIM3_Cap_Init(0XFFFF,72-1); //以1Mhz的频率计数
TIM1_PWM_Init(9999,143);// //延时初始化
ROLL=9500;//复位成90度
delay_ms(1000);
///测距/
while(1)
{
go(10);
temp=distance();
if(temp<30)//小于30
{
stop2();
}
}
}
void stop2(){
stop();
LEFTROLL_Init();
}
int distance(){
int temp;
while(1){
PAout(4)=1;
delay_us(15);
PAout(4)=0;
delay_ms(500);
if(TIM3CH1_CAPTURE_STA&0X80) //成功捕获到了一次高电平
{
temp=TIM3CH1_CAPTURE_STA&0X3F;
temp*=65536; //溢出时间总和
temp+=TIM3CH1_CAPTURE_VAL; //得到总的高电平的时间
temp=temp*240/10000;
printf("Distance:%d cm\r\n",temp);//打印总的高电平的时间,也就是转换后的高度了
TIM3CH1_CAPTURE_STA=0; //开启下一次捕获
return temp;
}
}
}
wave.h
#ifndef _TIMER_H
#define _TIMER_H
#include "sys.h"
#define ULTRASONIC_TRIG PAout(4)
#define ROLL TIM1->CCR1
void TIM3_Cap_Init(u16 arr,u16 psc);
void ultrasonic_Init(void);
void TIM1_PWM_Init(u16 arr,u16 psc);
void LEFTROLL_Init(void);
void RIGHTROLL_Init(void);
void stop2(void);
int distance(void);
void wave_init(void);
#endif
main.c
#include "move.h"
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "trailing.h"
#include "key.h"
#include "control.h"
#include "wave.h"
int main()
{
u8 mode;
Stm32_Clock_Init(9);
delay_init(72);
key_init();
uart_init(72,9600);
MOVE_Init(7199,0);//PWM频率=72000(719+1)=100Khz
left_PWM=100;
right_PWM=100;
stop();
while(1)
{
go(10);
mode=key();
printf("mode%d",mode);
switch(mode)
{
case 1:control();break;
case 2:trailing();break;
case 3:wave_init();
default:break;
}
}
}
整个工程
https://download.csdn.net/download/Harajukuuuu/12956248