赞
踩
提醒:本文章只叙述此小车相关大概内容(如模块的设置,C语言基础实现等),单片机详细教学不涉及。
循迹小车是学习单片机的“地基”,它能够让初学者认识单片机内部硬件结构及其功能,熟悉单片机的一些基础操作,如I/O的应用,定时中断与外部中断的应用等,同时也能让初学者对于C语言编程有更深的认识。我采用STM32F103C8T6、TB6612、TCRT5000三个主要模块进行小车组装,刚开始确实有很多问题,随着进一步深入,问题也迎刃而解了,所以我们学习这个小车,主要在于思想的转变和善于去研究,我相信很多过程中遇到的难题都会被我们解决的。
目录
最近也是参加电赛,所以所有的材料都是运用学校实验室提供的。
注意:本思维导图只针对于代码中各模块部分。
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电机为例):
AIN1 | 0 | 1 | 0 |
AIN2 | 0 | 0 | 1 |
停止 | 正转 | 反转 |
AO1、A02、BO1、BO2可以驱动电机,所以直接连接电机来让它们转起来!(注意别连反了,不然你的电机总是逆天的反转或者转圈~)
其实对于电机驱动模块最麻烦的就是PWM波的输入以及脉宽调制,具体体现在代码中。
因为在电路中有许多器件不能承受12V电压,所以我们需要将电源电压降到5V或者3.3V,这时我们就要用到降压模块,具体连接方法很明显,以LM2596为例,IN+连接电源正极,IN-连接电源负极,OUT+输出3.3V或者5V电压,OUT-输出GND。
相较于其它循迹方法,红外循迹较为简单,但也无法做到十分精准,本文以红外循迹为例,讲述循迹方法,希望各位同学能举一反三,继续学习!
它有四个引脚:VCC(接3~5V电压)、GND(接地)、D0(接单片机I/O口)、A0(模拟信号输出,一般不接)
话不多说,直接讲原理:当检测到黑线时,传感器上的指示灯灭掉,D0输出高电平返回到单片机上;当未检测到黑线时,传感器上的指示灯亮,D0输出低电平返回到单片机上。
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 header
-
- extern 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; //ARR
- TIM_TimeBaseInitStructure.TIM_Prescaler = 36 - 1; //PSC
- TIM_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; //CCR
-
-
- TIM_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; //CCR
-
- TIM_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);
- }
- }
- }
- }
- }
-
代码中有很多冗余,由于时间紧张所以就没有仔细去改写,各位同学可以根据实际情况去修改内容。
本次分享就到这里了,博主其实也是初学者,所以也非常希望各路大佬来批评指正,当然,如果我的文章能帮到您,请在评论区积极发言(手动狗头),这也是对我最大的鼓舞,谢谢!
链接:https://pan.baidu.com/s/1uPyZ8QkUlRGu5uMotWLBpQ?pwd=lfgp
提取码:lfgp
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。