网上的大部分stm32循迹小车都是VIP内容,也没有停车这个功能,于是我写了一个完整含有代码的一篇循迹小车,希望帮助大家更好的了解循迹小车。具体的L289N和循迹模块大家可以看其他博主的,组装B站也有视频。
材料:
循迹模块
工作电压:DC 3.3 ~ 5V
检测反射距离:1mm ~ 25mm 适用
模块中蓝色的电位器用于调节灵敏度,顺时针旋转,灵敏度变高;逆时针越小,灵敏度变低。
上电后电源指示灯(绿灯)亮。
有反射回来,DO 输出低电平,开关指示灯(绿灯)亮。
没反射回来,DO 输出高电平,开关指示灯(绿灯)灭。
引脚功能:
内容参考自循迹模块详解,详细内容可阅读此文章。
电机驱动
驱动采用L298N
供电控制:
只需要在12V供电处接上7-12V电压,供电GND处与单片机共地即可,5V供电处会输出一个5V的电压,可以用于给单片机供电,做小车时最常用的就是这种方式。使用这种方式时,板载5V使能不用管。
L298N详细信息大家可以看电机驱动----L298N,这里就不再重复了。
单片机
采用stm32f103RCT6(f1系列应该都可以用)。
套件
淘宝上有很多4轮三轮都行。
原理
简单来说就是当循迹模块检测到黑线后,低电平,单片机根据不同的循迹状态来改变PWM值进而改变直流电机电机的速度来完成拐弯操作。
比如0111,最左侧的循迹模块检测到黑线,车的位置偏右,应当向左拐。
代码:
电机驱动函数
PWM.c
#include "stm32f10x.h" // Device header
void PWM_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2|GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
TIM_InternalClockConfig(TIM2);
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = 100 - 1; //ARR
TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1; //PSC
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCStructInit(&TIM_OCInitStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 0; //CCR
TIM_OC1Init(TIM2, &TIM_OCInitStructure);
TIM_OC3Init(TIM2, &TIM_OCInitStructure);
TIM_Cmd(TIM2, ENABLE);
}
PWM.h
#ifndef __PWM_H
#define __PWM_H
#include "stm32f10x.h"
#define R(speed) TIM_SetCompare1(TIM2, speed);
#define L(speed) TIM_SetCompare3(TIM2, speed)
void PWM_Init(void);
#endif
Motor.h
#include "stm32f10x.h" // Device header
#include "PWM.h"
void Motor_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
PWM_Init();
}
void Motor_SetRightSpeed(int8_t Speed)
{
if (Speed >0)
{
GPIO_SetBits(GPIOA, GPIO_Pin_4);
GPIO_ResetBits(GPIOA, GPIO_Pin_5);
R(Speed);
}
else if(Speed==0){
GPIO_SetBits(GPIOA, GPIO_Pin_4);
GPIO_SetBits(GPIOA, GPIO_Pin_5);
R(Speed);
}else{
GPIO_ResetBits(GPIOA, GPIO_Pin_4);
GPIO_SetBits(GPIOA, GPIO_Pin_5);
R(-Speed);
}
}
void Motor_SetLeftSpeed(int8_t Speed)
{
if (Speed>0)
{
GPIO_SetBits(GPIOA, GPIO_Pin_6);
GPIO_ResetBits(GPIOA, GPIO_Pin_7);
L(Speed);
}
else if(Speed==0){
GPIO_SetBits(GPIOA, GPIO_Pin_6);
GPIO_SetBits(GPIOA, GPIO_Pin_7);
L(Speed);
}else{
GPIO_ResetBits(GPIOA, GPIO_Pin_6);
GPIO_SetBits(GPIOA, GPIO_Pin_7);
L(-Speed);
}
}
Motor.h
#ifndef __MOTOR_H
#define __MOTOR_H
#include "stm32f10x.h"
void Motor_Init(void);
void Motor_SetLeftSpeed(int8_t Speed);
void Motor_SetRightSpeed(int8_t Speed);
#endif
car.c
#include "stm32f10x.h" // Device header
#include "Motor.h"
#include "Delay.h"
void Car_Init()
{
Motor_Init();
}
void Go_Ahead(){
Motor_SetLeftSpeed(35);
Motor_SetRightSpeed(35);
}
void Turn_Right(){
Motor_SetRightSpeed(45);
Motor_SetLeftSpeed(-30);
}
void Turn_Left(){
Motor_SetLeftSpeed(45);
Motor_SetRightSpeed(-30);
}
void Self_Left(){
Motor_SetLeftSpeed(85);
Motor_SetRightSpeed(-85);
}
void Self_Right(){
Motor_SetLeftSpeed(-85);
Motor_SetRightSpeed(85);
}
void Car_Stop(){
Motor_SetLeftSpeed(0);
Motor_SetRightSpeed(0);
}
void Car_SlowDown(){
Motor_SetLeftSpeed(25);
Motor_SetRightSpeed(25);
}
小车的速度在car.c里面更改。
car.h
#ifndef __CAR_H
#define __CAR_H
void Car_Init(void);
void Self_Right(void);
void Self_Left(void);
void Turn_Right(void);
void Turn_Left(void);
void Go_Ahead(void);
void Car_Stop(void);
void Car_SlowDown(void);
#endif
传感器
Get.c
#include "stm32f10x.h" // Device header
void Get_Init()
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode =GPIO_Mode_IN_FLOATING ;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5| GPIO_Pin_6| GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
Get,h
#ifndef __Get_H
#define __Get_H
void Get_Init(void);
#endif
延时函数
采用阻塞延时(简单粗暴)。
Delay.c
#include "stm32f10x.h"
/**
* @brief 微秒级延时
* @param xus 延时时长,范围:0~233015
* @retval 无
*/
void Delay_us(uint32_t xus)
{
SysTick->LOAD = 72 * xus; //设置定时器重装值
SysTick->VAL = 0x00; //清空当前计数值
SysTick->CTRL = 0x00000005; //设置时钟源为HCLK,启动定时器
while(!(SysTick->CTRL & 0x00010000)); //等待计数到0
SysTick->CTRL = 0x00000004; //关闭定时器
}
/**
* @brief 毫秒级延时
* @param xms 延时时长,范围:0~4294967295
* @retval 无
*/
void Delay_ms(uint32_t xms)
{
while(xms--)
{
Delay_us(1000);
}
}
/**
* @brief 秒级延时
* @param xs 延时时长,范围:0~4294967295
* @retval 无
*/
void Delay_s(uint32_t xs)
{
while(xs--)
{
Delay_ms(1000);
}
}
Delay.h
#ifndef __DELAY_H
#define __DELAY_H
void Delay_us(uint32_t us);
void Delay_ms(uint32_t ms);
void Delay_s(uint32_t s);
#endif
主函数
main
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "Car.h"
#include "Get.h"
int L1,L2,R1,R2;
int count = 0; // 计数器,用于记录传感器状态变化的次数
int last_state = 0; // 记录上一次传感器状态
int current_state;
void read_sensors()
{
L1 = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_4);
L2 = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_5);
R1 = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_6);
R2 = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_7);
}
int main(void)
{
Get_Init();
Car_Init();
while(1)
{
read_sensors();
current_state = (L1 << 3) | (L2 << 2) | (R1 << 1) | R2;
// if (last_state == 0xF && current_state != 0xF)
// {
// count++; // 传感器状态变化,计数器加1
// Delay_ms(300);
// if (count >= 11)
// {
// Car_Stop(); // 计数器达到11,车辆停止
// while(1);
// }
// }
// else
// {
switch (current_state)
{
case 0x0:
Go_Ahead();
break;
case 0x8:
Self_Left();
Delay_ms(50);
break;
case 0xC:
Self_Left();
Delay_ms(50);
break;
case 0x4:
Turn_Left();
Delay_ms(10);
break;
case 0x2:
Turn_Right();
Delay_ms(10);
break;
case 0x1:
Self_Right();
Delay_ms(50);
break;
case 0x3:
Self_Right();
Delay_ms(50);
break;
case 0xF:
Car_SlowDown();
break;
case 0x6:
Car_SlowDown();
Delay_ms(50);
break;
default:
break;
}
}
//
// }
}
注释是停车功能,可根据赛道实际情况进行更改。(这个原理就是数赛道上赛道上的黑线,当检测到是其他状态而上一次是全黑后计数+1, 我们跑完两圈正好是10次,所以到11就停了)。
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/2302_81380153/article/details/144476652
|