基于STM32,TB6612,TCRT5000的简易红外循迹小车

        提醒:本文章只叙述此小车相关大概内容(如模块的设置,C语言基础实现等),单片机详细教学不涉及。

摘要

        循迹小车是学习单片机的“地基”,它能够让初学者认识单片机内部硬件结构及其功能,熟悉单片机的一些基础操作,如I/O的应用,定时中断与外部中断的应用等,同时也能让初学者对于C语言编程有更深的认识。我采用STM32F103C8T6、TB6612、TCRT5000三个主要模块进行小车组装,刚开始确实有很多问题,随着进一步深入,问题也迎刃而解了,所以我们学习这个小车,主要在于思想的转变和善于去研究,我相信很多过程中遇到的难题都会被我们解决的。

目录

摘要

一、材料选择

二、模块思维导图

三、主要材料概述

1、STM32F103C8T6最小系统板

2、TB6612电机驱动

3、LM2596S DC-DC直流可调降压模块

 4、TCRT5000红外循迹模块

5、显示屏模块

三、相关代码


一、材料选择

        最近也是参加电赛,所以所有的材料都是运用学校实验室提供的。

  1. 单片机:STM32F103C8T6;
  2. 电机驱动:TB6612或者L298(我选用TB6612);
  3. 降压模块:(可随意)LM2596;
  4. 循迹模块:TCRT5000(5个);
  5. 显示屏模块:四针脚I²C版本OLED;
  6. 四个电机加轮子;
  7. 杜邦线若干;
  8. 开关一个;
  9. 电池(12V)。

二、模块思维导图

    

         注意:本思维导图只针对于代码中各模块部分。

三、主要材料概述

1、STM32F103C8T6最小系统板

        STM32是一个微控制器产品系列的总称,目前这个系列中已经包含了多个子系列,分别是: STM32 小容量产品、STM32 中容量产品、 STM32 大容量产品和 STM32 互联型产品;按照功能上的划分,又可分为STM32F101xx、 STM32F102xx STM32F103xx 系列。
        STM32F103 C8T6引脚定义图:
        注意:
        (1)单片机有些引脚对应功能是特有的,如定时器的通道输出口,它们在使用的时候只需要初始化I/O即可;
        (2)单片机最大承受电压为5V,注意通电之前一定要查看降压模块的连接是否正确或者是否有降压模块。

2、TB6612电机驱动

        TB6612电机驱动共有16个个引脚。

        VM最大接15V电源,本博客接12V足以;

        VCC接3.3V或5V;

        GND不用说了吧,接地就行;

        PWMA、PWMB需要PWM波(方波),以控制A电机或B电机的速度,连接单片机时要注意PWM波输出的端口对应好,本文采用的是定时器TIM2的3、4通道,所以PWMA、PWMB分别连接PA2、PA3口,具体要参考你用的哪个定时器,然后根据引脚定义图对应好引脚位置;

        AIN1、AIN2、BIN1、BIN2用来控制电机的正反转,需要连接单片机的I/O口来给予它们高低电平,AIN控制A电机,BIN控制B电机,具体控制如下表(以控制A电机为例):

AIN1010
AIN2001
停止正转反转

        AO1、A02、BO1、BO2可以驱动电机,所以直接连接电机来让它们转起来!(注意别连反了,不然你的电机总是逆天的反转或者转圈~)

        其实对于电机驱动模块最麻烦的就是PWM波的输入以及脉宽调制,具体体现在代码中。

3、LM2596S DC-DC直流可调降压模块

        因为在电路中有许多器件不能承受12V电压,所以我们需要将电源电压降到5V或者3.3V,这时我们就要用到降压模块,具体连接方法很明显,以LM2596为例,IN+连接电源正极,IN-连接电源负极,OUT+输出3.3V或者5V电压,OUT-输出GND。

 4、TCRT5000红外循迹模块

        相较于其它循迹方法,红外循迹较为简单,但也无法做到十分精准,本文以红外循迹为例,讲述循迹方法,希望各位同学能举一反三,继续学习!

        

       它有四个引脚:VCC(接3~5V电压)、GND(接地)、D0(接单片机I/O口)、A0(模拟信号输出,一般不接)

        话不多说,直接讲原理:当检测到黑线时,传感器上的指示灯灭掉,D0输出高电平返回到单片机上;当未检测到黑线时,传感器上的指示灯亮,D0输出低电平返回到单片机上。

5、显示屏模块


三、相关代码

motor.c

#include "stm32f10x.h"                  // Device header
#include "pwm.h"//注意:增加某个函数要在对应.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;	//定义I/O口GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);PWM_Init();
}void Motor_LEFT_SetSpeed(int8_t Speed)		//左电机正反转
{if (Speed >= 0){GPIO_SetBits(GPIOA, GPIO_Pin_4);GPIO_ResetBits(GPIOA, GPIO_Pin_5);PWM_SetCompare3(Speed);}else{GPIO_ResetBits(GPIOA, GPIO_Pin_4);GPIO_SetBits(GPIOA, GPIO_Pin_5);PWM_SetCompare3(-Speed);}
}
void Motor_RIGHT_SetSpeed(int8_t Speed)		//右电机正反转
{if (Speed >= 0){GPIO_SetBits(GPIOA, GPIO_Pin_6);GPIO_ResetBits(GPIOA, GPIO_Pin_7);PWM_SetCompare4(Speed);}else{GPIO_ResetBits(GPIOA, GPIO_Pin_6);GPIO_SetBits(GPIOA, GPIO_Pin_7);PWM_SetCompare4(-Speed);}
}

pwm.c

#include "stm32f10x.h"                  // Device headerextern uint16_t Num;			//调用.c文件定义的Num变量
extern uint16_t t;
extern int FLAG;
void PWM_Init(void)
{RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;TIM_OCInitTypeDef TIM_OCInitStructure;GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;TIM_TimeBaseInitStructure.TIM_Period = 100 - 1;		//ARRTIM_TimeBaseInitStructure.TIM_Prescaler = 36 - 1;		//PSCTIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);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;		//CCRTIM_OC3Init(TIM2, &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;		//CCRTIM_OC4Init(TIM2, &TIM_OCInitStructure);TIM_Cmd(TIM2, ENABLE);
}
void Timer_Init(void)
{RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1;TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1;TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);TIM_ClearFlag(TIM3, TIM_FLAG_Update);TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);NVIC_PriorityGroupConfig(NVIC_PriorityGroup_3);NVIC_InitTypeDef NVIC_InitStructure;NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;NVIC_Init(&NVIC_InitStructure);TIM_Cmd(TIM3, ENABLE);
}
void TIM3_IRQHandler(void)
{if (TIM_GetITStatus(TIM3, TIM_IT_Update) == SET){Num ++;if(FLAG == 3){t = Num;}TIM_ClearITPendingBit(TIM3, TIM_IT_Update);}
}void PWM_SetCompare3(uint16_t Compare)
{TIM_SetCompare3(TIM2, Compare);
}void PWM_SetCompare4(uint16_t Compare)
{TIM_SetCompare4(TIM2, Compare);
}

main.c

#include "stm32f10x.h"
#include "delay.h"
#include "motor.h"
#include "tcrt5000.h"
#include "OLED.h"
#include "pwm.h"uint16_t t;
uint16_t Num;
uint16_t LEFT1,RIGHT1,LEFT2,RIGHT2,MIDDLE;
int FLAG = 0;
int main(void)
{	Timer_Init();	//初始化计时函数OLED_Init();	//初始化显示屏函数Motor_Init();	//初始化电机驱动函数tcrt5000_init();	//初始化红外循迹函数while (1){OLED_ShowString(2, 1, "TIME:");OLED_ShowNum(2, 6, Num, 5);RIGHT1 = GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_5);LEFT1 = GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_6);RIGHT2 = GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_7);LEFT2 = GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_8);MIDDLE = GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_9);if(LEFT1 == 0 && LEFT2 == 0 && MIDDLE == 1 && RIGHT2 == 0 && RIGHT1 == 0)				//直走{Motor_LEFT_SetSpeed(65);Motor_RIGHT_SetSpeed(65);}else if((LEFT1 == 1 || LEFT2 == 1) && MIDDLE == 0 && RIGHT2 == 0 && RIGHT1 == 0)		//左转{Motor_LEFT_SetSpeed(-50);Motor_RIGHT_SetSpeed(65);}else if(LEFT1 == 0 && LEFT2 == 0 && MIDDLE == 0 && (RIGHT1 == 1 || RIGHT2 == 1))		//右转{Motor_LEFT_SetSpeed(65);Motor_RIGHT_SetSpeed(-50);}else if(LEFT1 == 0 && LEFT2 == 1 && MIDDLE == 1 && RIGHT2 == 0 && RIGHT1 == 0)			//左转(增加灵敏度){Motor_LEFT_SetSpeed(-50);Motor_RIGHT_SetSpeed(65);}else if(LEFT1 == 1 && LEFT2 == 1 && MIDDLE == 1 && RIGHT2 == 0 && RIGHT1 == 0)			//左转(增加灵敏度){Motor_LEFT_SetSpeed(-50);Motor_RIGHT_SetSpeed(65);}else if(LEFT1 == 0 && LEFT2 == 0 && MIDDLE == 1 && RIGHT2 == 1 && RIGHT1 == 0)			//右转(增加灵敏度){Motor_LEFT_SetSpeed(65);Motor_RIGHT_SetSpeed(-50);}else if(LEFT1 == 0 && LEFT2 == 0 && MIDDLE == 1 && RIGHT2 == 1 && RIGHT1 == 1)			//右转(增加灵敏度){Motor_LEFT_SetSpeed(65);Motor_RIGHT_SetSpeed(-50);}else if(LEFT1 == 0 && LEFT2 == 0 && MIDDLE == 0 && RIGHT2 == 0 && RIGHT1 == 0)			//未探测到黑线的时候直走(防止未检测到黑线时不动){	Motor_LEFT_SetSpeed(65);Motor_RIGHT_SetSpeed(65);}else if(LEFT1 == 1 && LEFT2 == 1 && MIDDLE == 1 && RIGHT2 == 1 && RIGHT1 == 1)			//检测到横线{	if(FLAG == 0)							//标志位1(停止5秒后前进){Motor_LEFT_SetSpeed(0);Motor_RIGHT_SetSpeed(0);delay_init();OLED_Clear();OLED_ShowString(1,7,"READY");OLED_ShowNum(3,1,5,1);delay_ms(1000);OLED_ShowNum(3,3,4,1);delay_ms(1000);OLED_ShowNum(3,5,3,1);delay_ms(1000);OLED_ShowNum(3,7,2,1);delay_ms(1000);OLED_Clear();OLED_ShowString(3,8,"GO!");delay_ms(1000);OLED_Clear();Motor_LEFT_SetSpeed(30);delay_ms(100);Motor_RIGHT_SetSpeed(30);delay_ms(100);FLAG ++;}else if(FLAG == 1)					//标志位2(停止5秒后前进){Motor_LEFT_SetSpeed(0);Motor_RIGHT_SetSpeed(0);delay_init();OLED_Clear();OLED_ShowString(1,7,"READY");OLED_ShowNum(3,1,5,1);delay_ms(1000);OLED_ShowNum(3,3,4,1);delay_ms(1000);OLED_ShowNum(3,5,3,1);delay_ms(1000);OLED_ShowNum(3,7,2,1);delay_ms(1000);OLED_Clear();OLED_ShowString(3,8,"GO!");delay_ms(1000);OLED_Clear();Motor_LEFT_SetSpeed(80);delay_ms(500);Motor_RIGHT_SetSpeed(80);delay_ms(500);FLAG ++;}else if(FLAG == 2)					//标志位3(直走){delay_init();OLED_ShowString(1,5,"STRINGHT");OLED_ShowString(2, 1, "TIME:");OLED_ShowNum(2, 6, Num, 5);Motor_LEFT_SetSpeed(85);Motor_RIGHT_SetSpeed(85);delay_ms(500);OLED_Clear();FLAG ++;}else if(FLAG == 3)					//标志位4(比赛结束,停止){	FLAG = 0;int i;for(i = 0;i >= 0;i++){OLED_ShowString(1,5,"STOP");OLED_ShowString(2, 1, "TIME:");OLED_ShowNum(2, 6, t, 5);Motor_LEFT_SetSpeed(0);Motor_RIGHT_SetSpeed(0);}}}	}
}

        代码中有很多冗余,由于时间紧张所以就没有仔细去改写,各位同学可以根据实际情况去修改内容。

       ( 另附:想要工程文件的同学可以评论邮箱,内含小车视频哦~转载请标明出处。)


        本次分享就到这里了,博主其实也是初学者,所以也非常希望各路大佬来批评指正,当然,如果我的文章能帮到您,请在评论区积极发言(手动狗头),这也是对我最大的鼓舞,谢谢!

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

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

相关文章

Python 面向对象(1)

软件编程的实质是将我们的思维转变成计算机能够识别语言的一个过程。 目录 面向过程 面向对象 理解面向对象 面向过程: 面向对象: 面向对象的特点 完成需求时: 类的设计 类与对象的关系 创建类 设计类 类的介绍 格式 创建类实例…

三勾点餐 | 开源点餐系统、扫码点餐系统、收银台点餐系统

项目介绍 三勾点餐系统基于thinkphp8element-plusuniapp打造的面向开发的小程序商城,方便二次开发或直接使用,可发布到多端,包括微信小程序、微信公众号、QQ小程序、支付宝小程序、字节跳动小程序、百度小程序、android端、ios端。 简单实用…

太阳能供电+4G摄像头搭建EasyCVR鱼塘养殖远程视频监控方案

一、背景需求 随着我国农业的快速发展,以及对新兴技术的应用,养殖业、农牧业、种植业等也面临着全新的挑战与机遇。对鱼塘养殖行业来说,养殖区域面积大、管理难,经常会遇到偷钓者、盗窃鱼苗、非法入侵等监管难题。在国家大力扶持…

【Python机器学习系列】拟合和回归傻傻分不清?一文带你彻底搞懂它

一、拟合和回归的区别 拟合并不特指某一种方法,指的是对一些数据,按其规律方程化,比如把平面(一元)上一系列的离散点,用一条直线(线性)或光滑的曲线(非线性)连…

【解析rosbag可视化】二维图像可视化点云

文章目录 前言一、环境配置二、代码三、效果图 前言 一般我们把采集的原始数据放在 rosbag 中。我们要可视化需要对 bag 包解析二维图像可视化点云思路:在二维图片显示投影的点云就行了 一、环境配置 我用的 python 3.7pip install --extra-index-url https://rospy…

众和策略:短线交易看什么?短线交易看什么指标?

短线交易看什么? 1、k线 当k线出现黄昏十字星、黑乌鸦、乌云盖顶等卖出形状图时,是一种卖出信号,当k线出现早晨十字星、红三兵、等买入形状图时,是一种买入信号。 2、均线 当均线出现死叉、空头摆放时是一种卖出信号&#xff…

ansible ( 2 )

1、command模块 一般用于执行linux命令,不支持管道符和重定向 2、shell模块 相当于command模块的升级版,也可以支持linux命令。支持管道符和重定向 3、cron模块 在远程主机生成定时任务 分 时 日 月 周 minute hour day month weekday job name…

NVMe前世今生

NVMe前世今生 1 NVMe的由来2 NVMe工作原理3 NVMe优点4 NVMe适用场景 本文属于《 NVMe协议基础系列教程》之一,欢迎查看其它文章。 1 NVMe的由来 目前机械硬盘大多数使用 SATA (Serial ATA Advanced Host Controller Interface) 接口,接口协议为 AHCI&a…

鸿蒙-arkTs:开发工具安装

编辑器下载:官网下载地址https://developer.harmonyos.com/cn/develop/deveco-studio/#download 以该版本为例,下载自己电脑对应的系统版本即可; 后续编辑器中的SDK,模拟器等,逐步安装即可; 汉化 编辑器左…

arcgis javascript api4.x加载天地图cgs2000坐标系

需求&#xff1a;arcgis javascript api4.x加载天地图cgs2000坐标系 效果&#xff1a; 示例代码&#xff1a; <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"wid…

C# 实时存储16进制数据,写入文件格式为Raw

1.示例代码 private void button1_Click(object sender, EventArgs e){byte[] data { 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28 }; //模拟要写入的数组List<byte[]> listBytes new List<byte[]>();listBytes.Add(data); //数组转集合RecData(listBytes);…

TensorRT 简单介绍

一、TensorRT 对于算法工程师来说&#xff0c;相信大家已经对TensorRT耳熟能详了&#xff0c;那么这个TensorRT是什么呢&#xff1f; 其实&#xff0c;TensorRT是一个可以在NVIDIA各种GPU硬件平台下运行的推理引擎&#xff0c;同时也是一个高性能的深度学习推理优化器&#x…