stm32循迹小车

目录

材料:

循迹模块

电机驱动

单片机

套件

原理

代码:

电机驱动函数

传感器

延时函数

主函数


网上的大部分stm32循迹小车都是VIP内容,也没有停车这个功能,于是我写了一个完整含有代码的一篇循迹小车,希望帮助大家更好的了解循迹小车。具体的L289N和循迹模块大家可以看其他博主的,组装B站也有视频。

材料:

循迹模块

工作电压:DC 3.3 ~ 5V

检测反射距离:1mm ~ 25mm 适用

模块中蓝色的电位器用于调节灵敏度,顺时针旋转,灵敏度变高;逆时针越小,灵敏度变低。

上电后电源指示灯(绿灯)亮。

有反射回来,DO 输出低电平,开关指示灯(绿灯)亮。

没反射回来,DO 输出高电平,开关指示灯(绿灯)灭。

引脚功能:

|-----|--------------------|
| VCC | 3.3/5V |
| GND | GND |
| D0 | 任意 GPIO 口 |
| A0 | 有 ADC 功能的引脚(一般不使用) |

内容参考自循迹模块详解,详细内容可阅读此文章。

电机驱动

驱动采用L298N

供电控制:

只需要在12V供电 处接上7-12V电压,供电GND 处与单片机共地 即可,5V供电 处会输出一个5V的电压,可以用于给单片机供电,做小车时最常用的就是这种方式。使用这种方式时,板载5V使能 不用管**。**

|-----------|-----------|
| L298N | 电源 |
| 12V | 电源12V |
| GND | 电源负 |
| L298N | stm32 |
| OUT1和OUT2 | 电机1的两个端子 |
| OUT3和OUT4 | 电机2的两个端子 |
| ENA | PA0 |
| ENB | PA2 |
| IN1和IN2 | PA4和PA5 |
| IN3和IN4 | PA6和PA7 |
| VSS | 5V |
| GND | GND |

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就停了)。

希望可以帮助到有需要的人。(下次想写智能风扇or桌宠,大概在寒假而且所有博客都会免费的)

相关推荐
polarislove02149 小时前
10.1 [ADC] 逐次逼近型ADC-嵌入式铁头山羊STM32笔记
笔记·stm32·嵌入式硬件
qq_672592759 小时前
STM32超声测距离的测量精度评估
stm32·硬件架构·硬件工程
谈思汽车9 小时前
TEE or HSM/SE?车载安全技术选型指南
嵌入式硬件·安全·智能汽车·可信执行环境·汽车信息安全
单片机系统设计9 小时前
基于STM32的智能垃圾桶/语音分类/自动开盖/矩阵按键
stm32·矩阵·毕业设计·语音识别·智能垃圾桶
-曾牛10 小时前
【汇编语言入门】从第一个加法程序吃透汇编核心基础
汇编·单片机·嵌入式硬件·汇编语言·病毒分析·lcx·逆向开发
IT方大同10 小时前
ADC&DAC概述
嵌入式硬件
三品吉他手会点灯11 小时前
STM32F103 学习笔记-21-串口通信(第3节)-STM32串口初始化结构体和固件库讲解
笔记·stm32·单片机·嵌入式硬件·学习
Lester_110111 小时前
单片机EEPROM写入数据之前为什么要先擦除?
单片机·嵌入式软件
点灯小铭11 小时前
基于单片机的多功能LCD万年历时钟设计与温度显示系统
单片机·嵌入式硬件·毕业设计·课程设计·期末大作业
点灯小铭11 小时前
基于单片机的玉米播种机漏播检测装置设计与实现
单片机·嵌入式硬件·毕业设计·课程设计·期末大作业