STM32、51循迹小车 【STM32入门控制必会 含51全部程序】

前言

故事的由来的从给学弟学妹门辅导一次单片机综合实验课开始,由于笔者比较喜欢电子控制类的东西,自进入学校就迷上了,后来无意间加入了学校众创空间下的一个工作室成为一员,至此开启了嵌入式学习之路。。。。。。。
以下为了减少文章篇幅,主要展示51程序,STM32程序文件 详见文末说明。

方案设计

不废话了,下面为了增加内容,摘点小学弟的实验报告(doge~)

我们小组使用光电传感器来采集路面信息。使用红外光电对管,其结构简明,实现方便,成本低廉,没有复杂的图像处理工作,因此反应灵敏,响应时间少。但也存在不足,它能获取的信息是不完全的,容易受很多扰动(如背景光源,高度等)的影响,抗干扰能力较差。但本次实验要求精度并不是太高,出于成本和设计复杂度方面考虑,我们决定采用红外对管来完成传感器模块。
下图为一光电循迹模块的基本电路原理图:在这里插入图片描述
两节18650电池组成的7.4V锂电池组,因为锂电池性能稳定,还可以充电,循环使用,关键是动力强,就算经过PWM调制也能够稳定驱动小车循迹。(这里由于开环控制
电机采用直流有刷电机,其转动力矩大,体积小,重量轻,装配简单,操作方便。速度的调节可以改变电压也可以调节PWM,搭配L298电机驱动模块组装方便。(其实就是TT减速电机

逻辑分析

检测到非黑色路面的红外接收头处理后送出的是低电平,而检测到黑色路线的检测头送出的是高电平,由此可根据这两个红外接收头的高低电平判断路线情况而调整小车前进方向。具体情况有如下几种:在这里插入图片描述

程序编写

打个头,也就值我给他们讲解的程序,课堂上临时写的,可能有些仓促不够完美。

C51程序—模块化便于移植

Car.h文件:

#ifndef  _CAR_H_
#define	 _CAR_H_

#include <STC89C5xRC.H>
typedef  unsigned char 	u8;
typedef  unsigned int	 	u16;
/******   四轮请去掉注释 **********/
//#define _4X_
/* L298N逻辑引脚定义 */
sbit IN1 = P1^2;
sbit IN2 = P1^3;
sbit IN3 = P1^4;
sbit IN4 = P1^5;
/* 循迹模块输入 */
sbit INL = P3^0;
sbit INR = P3^1;
//小车根据循迹模块自主控制
void Car_Scan();    //小车
#endif

Car.c文件:

#include "Car.h"

//小车根据循迹模块自主控制
void Car_Scan()   			//小车
{		
	if(INL==0&&INR==0)  	//直行
		{	
			IN1=1;	IN2=0;		//左论正转
			IN3=1;	IN4=0;		//右轮正转
		}
#ifdef _4X_
//四轮车 两驱
	if(INL==1&&INR==0)  	// 左边检测到,向左校正
		{
			IN1=0;	IN2=1;		//左轮反转
			IN3=1;	IN4=0;		//右轮正转
		}

	if(INL==0&&INR==1)  	// 右边检测到,向右校正
		{	
			IN1=1;	IN2=0;		//左论正转
			IN3=0;	IN4=1;		//右轮反转
		}
#else 
// 三轮  两驱
	if(INL==1&&INR==0)  	// 左边检测到,向左校正
		{
			IN1=1;	IN2=1;		//左轮停转
			IN3=1;	IN4=0;		//右轮正转		
		}
	if(INL==0&&INR==1)  	// 右边检测到,向右校正
		{	
			IN1=1;	IN2=0;		//左论正转
			IN3=1;	IN4=1;		//右轮停转
		}
#endif
	if(INL==1&&INR==1)  // 两边检测到或小车离地,停车
		{	
			IN1=1;	IN2=1;	
			IN3=1;	IN4=1;
		}
}

pwm.h文件 这里利用定时器0产生

#ifndef	_PWM_H
#define _PWM_H

#include <STC89C5xRC.H>
#include "Car.h"

extern u8 PWM;
extern u8 time;
sbit Pwm0 = P1^0;			//PWM输出引脚
sbit Pwm1 = P1^1;			//PWM输出引脚
void Pwm_Init();
#endif

pwm.c文件 ,大多数人拿到的是89C51,还有部分STC12的,尽管12已经带有硬件PWM功能了,但为了方便 这里默认老方式向STC89C51看起了,注意这个程序给12C51用需要将其设置为12T模式,这是为了使得工作步调相同,也就不同修改程序了。

#include "pwm.h"
#include "Car.h"

u8 PWM = 50;							//pwm占空比为50%,可调占空比
u8 time = 0;

void Pwm_Init()       // 100us中断,8位自动重载
{	 
	AUXR &= 0x7F;				//定时器时钟12T模式
	TMOD &= 0xF0;				//设置定时器模式
	TMOD |= 0x02;				//设置定时器模式 8位重载
	TL0 = 0xA4;					//设置定时初始值
	TH0 = 0xA4;					//设置定时重载值
	TF0 = 0;						//清除TF0标志
	EA=1;            		//总中断打开
	ET0=1;           		//定时器0中断使能
	TR0=1;           		//定时器0开关打开
 }

void Timer0_PwmGo() interrupt 1
{
	time++;
	if(time >= 100)	time = 0; 		//PWM周期为100us*100=100Hz=0.01s=10ms
	if(time < PWM)		
		{
			Pwm0 = 1;				//高电平
			Pwm1 = 1;
		}
    if(time >= PWM)
		{
			Pwm0 = 0;				//低电平
			Pwm1 = 0;	
		}
}

主程序 main.c

#include "Car.h"
#include "pwm.h"

void main()
{
	Pwm_Init();				//标值100,占空比50%
	while(1)
	{
		Car_Scan();         //循迹扫描
	}
}

STM32程序

Car.h 文件 主要定义一些IO和相关函数声明

#ifndef __CAR_H
#define __CAR_H	 
#include "sys.h"

#define RIN_L   PAin(0)	    //PA0   左边 循迹模块
#define RIN_R   PAin(1)	    //PA1 	右边 循迹模块

//顺便定义一下 L298N 的四个输出 引脚
#define IN1  		PAout(2)	    //PA2   左边 电机
#define IN2  		PAout(3)	    //PA3 	
#define IN3  		PAout(4)	    //PA4   右边 电机
#define IN4  		PAout(5)	    //PA5 	

// 小车 函数声明
void Forward(void);
void Retreat(void);
void Turn_Left(void);
void Turn_Right(void);
void Stop(void);
										   
void Car_Init(void);                //IO初始化
void Car_Scan(void);			    //循迹小车 执行函数
#endif

Car.c文件:

#include "stm32f10x.h"
#include "key.h"
#include "sys.h" 
#include "delay.h"
						    
//按键初始化函数
void KEY_Init(void)          //IO初始化
{ 
 	GPIO_InitTypeDef GPIO_InitStructure;
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);   //使能端口A时钟
      //初始化 WK_UP-->GPIOA.0-1 输入
	GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_0|GPIO_Pin_1;  //KEY0-KEY1
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;          //设置成上拉输入
 	GPIO_Init(GPIOA, &GPIO_InitStructure);					//初始化GPIOA0,1
	  //初始化 WK_UP-->GPIOA.2-5 输出
	GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 	 	//PP-通用推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;	     //IO口速度为50MHz(最大)
    GPIO_Init(GPIOA, &GPIO_InitStructure);	
	GPIO_SetBits(GPIOA,GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5); //全部初始化为高

}
// =========== 小车 循迹模块状态扫描 ================
void Car_Scan()
{
	/*.....................*/
}
/*   小车 行动函数  */

由于循迹逻辑 和小车行动函数和51一模一样,此处不再展示-----------------------
其实这样就可以跑了,这里给大家放上串口控制的程序,这样就可与搭配蓝牙模块实现遥控小车功能!

添加串口程序变身STM32遥控小车

UART.h文件

#define __USART_H
#include "stdio.h"	 //printf
#include "sys.h" 

#define USART_REC_LEN  	200  	            //定义最大接收字节数 200
#define EN_USART1_RX 		1		        //使能(1)/禁止(0)串口1接收
	  	
extern u8  USART_RX_BUF[USART_REC_LEN];     //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符 
extern u16 USART_RX_STA;         	    	//接收状态标记	
extern u8  RxDated;
 
#define SendOK    USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET    //发送完毕中断标志
#define ReciveOK  USART_GetFlagStatus(USART1,USART_IT_RXNE)         //接收完毕中断标志

//如果想串口中断接收,请不要注释以下宏定义
void USART1_Init(u32 BaudRateS);
#endif

UART.c文件,这里主要是串口接收 数据处理,以及对接收到的苏数据分析

#include "sys.h"
#include "usart1.h"	
#include "led.h"
#include "key.h"   //包含 小车逻辑 引脚定义
 
//如果使用ucos,则包括下面的头文件即可.
#if SYSTEM_SUPPORT_OS
#include "includes.h"	//ucos 使用	  
#endif

//加入以下代码,支持printf函数,而不需要选择use MicroLIB	  
#if 1
#pragma import(__use_no_semihosting)             
//标准库需要的支持函数                 
struct __FILE 
{ 
	int handle; 

}; 

FILE __stdout;       
    
void _sys_exit(int x)          //定义_sys_exit()以避免使用半主机模式
{ 
	x = x; 
} 
//重定义fputc函数 
int fputc(int ch, FILE *f)
{      
	while((USART1->SR&0X40)==0) 
        ; //循环发送,直到发送完毕   
    USART1->DR = (u8) ch;      
	return ch;
}
#endif 

//使用microLib的方法
 /* 
int fputc(int ch, FILE *f)
{
	USART_SendData(USART1, (uint8_t) ch);

	while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET) {}	
   
    return ch;
}
int GetKey (void)  { 

    while (!(USART1->SR & USART_FLAG_RXNE));

    return ((int)(USART1->DR & 0x1FF));
}
*/
 
#if EN_USART1_RX                     //如果使能了接收

 u8 USART_RX_BUF[USART_REC_LEN];     //定义一个长度为USART_REC_LEN的串口接收缓冲字符数组,将接收到的数据存入其中,
                                     //长度为USART_REC_LEN个字节,具体长度依据实际数据,默认设为200.
 
 /************************************************
 * 串口1中断服务程序
 * 注意,读取USARTx->SR能避免莫名其妙的错误  
 * u8 USART_RX_BUF[USART_REC_LEN] 
 * 接收状态
 * bit15,	接收完成标志
 * bit14,	接收到0x0d
 * bit13~0,接收到的有效字节数目
 *************************************************/
u16 USART_RX_STA=0;                 //接收状态标记	  
  
void USART1_Init(u32 BaudRateS){
      //GPIO端口设置
    GPIO_InitTypeDef GPIO_InitStructure;                        //GPIO结构体变量
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	 
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);	//使能USART1,GPIOA时钟
  
	    //USART1_TX   GPIOA.9初始化
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;                   //PA.9
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;           
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;    	        //复用推挽输出
    GPIO_Init(GPIOA, &GPIO_InitStructure);                      //初始化GPIOA.9
                                                                                                                        
        //USART1_RX	  GPIOA.10初始化                            
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;                  //PA10            
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;       //浮空输入
    GPIO_Init(GPIOA, &GPIO_InitStructure);                      //初始化GPIOA.10  
                                                                                                                        
        //Usart1 NVIC 配置                                      
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;          
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;    //抢占优先级3
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;		    //子(响应)优先级3
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			    //IRQ通道使能
	NVIC_Init(&NVIC_InitStructure);	                            //根据指定的参数初始化VIC寄存器
  
     //USART 初始化设置

	USART_InitStructure.USART_BaudRate = BaudRateS;             //串口波特率
	USART_InitStructure.USART_WordLength = USART_WordLength_8b; //字长为8位数据格式
	USART_InitStructure.USART_StopBits = USART_StopBits_1;      //一个停止位
	USART_InitStructure.USART_Parity = USART_Parity_No;         //无奇偶校验位
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //无硬件数据流控制
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;	                //收发模式

    USART_Init(USART1, &USART_InitStructure);                   //初始化串口1
    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);              //开启串口接受中断
    USART_Cmd(USART1, ENABLE);                                  //使能串口1 

}
    //串口1中断服务程序,串口1有数据来了要干的事
void USART1_IRQHandler(void)
	{
#if SYSTEM_SUPPORT_OS 	   //如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
	OSIntEnter();    
#endif
	u8 RxDated, RxDate; 			       //定义一个8位的变量来存放数据
		  //此处定义了一种接收协议 (接收到的数据必须是0x0d 0x0a结尾,不是则重新接收),保证数据完整性
	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)           //接收中断
		{
		RxDated =USART_ReceiveData(USART1);	                        //读取接收到的数据赋给 RxDated
		
		if((USART_RX_STA&0x8000)==0)                                //接收未完成
			{
			if(USART_RX_STA&0x4000)                                 //接收到了0x0d
				{
				  if(RxDated!=0x0a)USART_RX_STA=0;                  //接收错误,重新开始
				  else USART_RX_STA|=0x8000;	//一个字节接收成功 0x8000 即换行\r\n,可作为数据收发结束的标志     
				}
			    else              //还没收到0X0D,即还未完全(完整)接收数据
				     {	
				       if(RxDated==0x0d)USART_RX_STA|=0x4000;
				       else
					       {
				      	   USART_RX_BUF[USART_RX_STA&0X3FFF]=RxDated; //将接收到的数据存入USART_RX_BUF数组
					         USART_RX_STA++;
				      	   if(USART_RX_STA>(USART_REC_LEN-1))
										 USART_RX_STA=0;               //接收数据错误,重新开始接收	  
					       }		 
			    	}
			}   		 
     } 
/*******  用户串口任务执行段  **********/
	if(USART_GetFlagStatus(USART1,USART_IT_RXNE))           //若已完成数据接收  
		{
            RxDate = *USART_RX_BUF;  //将字符数组中的数据读取出来        

		switch(RxDate)         //switch 数据处理函数
				{ 
				case 'U': Forward();
					break;
				case 'D': Retreat();
					break;	
				case 'L': Turn_Left();
					break;
				case 'R': Turn_Right();
					break;					
				case 'S': Stop();
					break;
		/************************/
				case '0': LED1=1;
					printf("灯已关闭\r\n"); 
					break;
				case '1': LED1=0;
					printf("灯已打开\r\n");
					break;			
			  }	
		}
	//执行任务结束	 
		 
#if SYSTEM_SUPPORT_OS 	   //如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
	OSIntExit();  											 
#endif
} 
#endif	

主程序main.c

/************************************************
 * 简单遥控+循迹小车程序
 *  接线:两路循迹模块输入引脚      PA1-PA2
 *		  L298N电机控制引脚       PA2-PA5
 *		 TIM3用作PWM输出			PA6-PA7
 *  平台:STM32F103C8T6核心板
 * @作者: Guard_Byte
************************************************/

#include "led.h"
#include "key.h"
#include "sys.h"
#include "delay.h"
#include "usart1.h"
#include "timer.h"

 int main(void)
 {		
      NVIC_Group2;           //设置NVIC中断分组2;2位抢占优先级,2位响应优先级
	  USART1_Init(9600);	 //串口1初始化为9600
	  PWM_TIM3_Inits(99,36-1); //PWM频率 20kHz 占空比 0到100 可调
	  delay_init();          //延时函数初始化
   	  LED_Init();			 //LED端口初始化
	  KEY_Init();            //初始化与按键连接的硬件接口,包括小车I/O定义
	
	  LED1=1;
	  while(1)
	   {
            Car_Scan();
	   }	
 }

结语

本来写STM32的,没想到51程序贴太多了,直接这里就不多废话了,文件自由下载
百度网盘链接:https://pan.baidu.com/s/1PXEE60j1ru0KjL39TY__kw?pwd=bxmw
提取码:bxmw

个人代码仓库 https://gitee.com/Guard_Byte/stm32-f103-learning-record/tree/master
项目文件夹 目录下

  • 8
    点赞
  • 90
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
STM32视觉循迹小车是一种基于STM32单片机的智能车辆,通过搭载视觉系统实现对线路的检测和跟踪,具备实时感知环境的能力。下面我会用300字回答这个问题。 首先,STM32视觉循迹小车是一种集成了图像处理和控制系统的智能车辆。通过搭载摄像头和视觉处理模块,它可以实时拍摄道路图像,并对图像进行处理分析,实现对线路的检测和跟踪。 其次,STM32单片机作为控制中心,可以根据视觉系统提供的图像信息,实时控制小车的行驶方向和速度。当小车检测到道路上的线路时,STM32会通过算法判断线路的位置和方向,并发送指令给电机控制模块,使小车按照线路行驶。 此外,STM32视觉循迹小车还具备一些其他功能。例如,它可以通过无线通信模块与智能手机或电脑进行连接,实现远程控制和监控。同时,它还可以搭载传感器模块,实现对环境的感知,例如避障功能。 最后,STM32视觉循迹小车在教育、研究和娱乐领域有着广泛的应用。在教育方面,它可以帮助学生理解和掌握嵌入式系统、控制算法等相关知识。在研究方面,它可以用于机器人视觉、自动驾驶等领域的研究。在娱乐方面,它可以作为一种趣味玩具,提供给用户进行娱乐和休闲。 综上所述,STM32视觉循迹小车是一种基于STM32单片机的智能车辆,通过视觉系统实现对线路的检测和跟踪,并通过STM32进行实时控制。它具备远程控制、传感器扩展等功能,广泛应用于教育、研究和娱乐等领域。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Guard_Byte

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值