基于STM32F103C8T6的红外循迹避障小车

您所在的位置:网站首页 循迹小车红外传感器模块 基于STM32F103C8T6的红外循迹避障小车

基于STM32F103C8T6的红外循迹避障小车

2024-06-14 22:50| 来源: 网络整理| 查看: 265

一,硬件原理图设计

二,代码实现 1.电机

        编程思路:控制核心板IO口的电平(即电机驱动模块的AIN1,AIN2,BIN1,BIN2)从而控制AO1、AO2、BO1、BO2的电平高低,进而控制电机使其正转、反转、不转。由于单纯地拉高拉低电平只能让电机在转与不转的状态切换,故决定利用TIM4的两个通道分别产生两路PWM信号来控制电机速度,四个电机两个一组,同一侧的两个连接在同一个PWM上。(#include "dianji.h"文件中同时写了对循迹模块引脚的初始化)

#ifndef _DIANJI_H #define _DIANJI_H #include "sys.h" #define IN1 PCin(13) #define IN2 PCin(14) #define IN3 PCin(15) #define IN4 PBin(12) void GPIO_XUJI_Init(void); void Motor_Init(void); void PWM_Init(void); void PWM_SetCompare(int16_t Compare); void Motor_SetSpeed1(float Speed); void Motor_SetSpeed2(float Speed); #endif #include "dianji.h" //循迹模块引脚初始化 void GPIO_XUJI_Init(void){ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOB, ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14| GPIO_Pin_15; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOC, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); } //电机引脚初始化 void Motor_Init(void) { //电机正反转引脚配置 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9| GPIO_Pin_5| GPIO_Pin_4; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); PWM_Init(); } //PWM初始化 void PWM_Init(void) { RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); //开启定时器2的时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //开启GPIO时钟 /********** PWM引脚的GPIO配置 **********/ GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //对定时器GPIO设置为复用推挽输出 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7|GPIO_Pin_6; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); TIM_InternalClockConfig(TIM4); //TIM4配置 TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; //TIM4配置 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(TIM4, &TIM_TimeBaseInitStructure); //C8T6的主频为72MHZ,设置的PWM频率为 72M/((ARR+1)*(PSC+1))=72M/(100*36)=20KHZ //TIM4的通道1配置 TIM_OCInitTypeDef TIM_OCInitStructure; //TIM_OCStructInit(&TIM_OCInitStructure); TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;//PWM1模式 TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //设置极性为高 TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能 TIM_OCInitStructure.TIM_Pulse = 0; //CCR TIM_OC1Init(TIM4, &TIM_OCInitStructure); TIM_OC1PreloadConfig(TIM4,TIM_OCPreload_Enable);//使能预装载寄存器 //TIM4的通道2配置 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;//PWM1模式 TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //设置极性为高 TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能 TIM_OCInitStructure.TIM_Pulse = 0; //CCR TIM_OC2Init(TIM4, &TIM_OCInitStructure); TIM_OC2PreloadConfig(TIM4,TIM_OCPreload_Enable);//使能预装载寄存器 TIM_Cmd(TIM4, ENABLE); //使能TIM4 } //设置占空比 void PWM_SetCompare(int16_t Compare) { TIM_SetCompare2(TIM4, Compare); TIM_SetCompare1(TIM4, Compare); } void Motor_SetSpeed1(float Speed) //设置电机1 2 转速,有符号量,正反转 { if (Speed 0) { GPIO_ResetBits(GPIOB, GPIO_Pin_8); GPIO_SetBits(GPIOB, GPIO_Pin_9);//正转 PWM_SetCompare(Speed); } else if(Speed == 0) { GPIO_SetBits(GPIOB, GPIO_Pin_8); GPIO_SetBits(GPIOB, GPIO_Pin_9);//停止 PWM_SetCompare(100); } } void Motor_SetSpeed2(float Speed) //设置电机3 4 转速,有符号量,正反转 { if (Speed0) { GPIO_ResetBits(GPIOB, GPIO_Pin_5); GPIO_SetBits(GPIOB, GPIO_Pin_4);//正转 PWM_SetCompare(Speed); } else if(Speed == 0) { GPIO_SetBits(GPIOB, GPIO_Pin_5); GPIO_SetBits(GPIOB, GPIO_Pin_4);//停止 PWM_SetCompare(100); } } 2.红外循迹

        编程思路:检测到黑线时红外模块将会返回低电平,否则返回高电平。 故只需要读取对应的IO端口电平用以控制小车转向从而实现巡线。(我使用的是四路循迹,中间两路用来检测黑线,两边的两路用来检测非黑线)

循迹逻辑:(黑色路线宽度为:大于P2到P3之间距离,小于P1到P4之间距离) 

case1:P2,P3检测到黑线,P1,P4未检测到黑线时,所有电机都正转

case2:P1,P2,P3检测到黑线,P4未检测到黑线时;或者P1,P2检测到黑线,P3,P4未检测 到黑线时;或者P1检测到黑线,P2,P3,P4未检测时;或者P2检测到黑线,P1,P3,P4未检测时到黑线时;以上四种情况皆为 P4侧电机正转,P1侧电机反转,使车身向P1侧转向。

case3:P2,P3,P4检测到黑线,P1未检测到黑线时;或者P3,P4检测到黑线,P1,P2未检测 到黑线时;或者P4检测到黑线,P1,P2,P3未检测时;或者P3检测到黑线,P1,P2,P4未检测时到黑线时;以上四种情况皆为 P4侧电机反转,P1侧电机正转,使车身向P4侧转向。

case4:其他所有情况,皆为所有电机都停止(包括P1,P2,P3,P4都检测到黑线的情况,由于自然光的原因,在车轮未放在地上时, P1,P2,P3,P4都会检测到自然光中黑线,故该情况下所有电机也都停止)

if((!IN1)&&(!IN3)&&IN2&&IN4){ Motor_SetSpeed1(35); Motor_SetSpeed2(35); } else if(((!IN1)&&(!IN3)&&(!IN4)&&(IN2)) || ((!IN3)&&(!IN4)&&(IN1)&&(IN2))||((!IN3)&&(IN1)&&(IN2)&&(IN4)) || ((!IN4)&&(IN1)&&(IN3)&&(IN2))){ Motor_SetSpeed1(30); Motor_SetSpeed2(-30);//右转 } else if(((!IN1)&&(!IN3)&&(!IN2)&&(IN4)) || ((!IN1)&&(IN3)&&(!IN2)&&(IN4))||((!IN1)&&(IN3)&&(IN2)&&(IN4)) || ((IN1)&&(IN3)&&(!IN2)&&(IN4))){ Motor_SetSpeed1(-30); Motor_SetSpeed2(30);//左转 } else{ Motor_SetSpeed1(0); Motor_SetSpeed2(0); } 3.避障

        编程思路:红外避障模块检测到障碍时,会返回一个低电平。通过判断IO口的电平继而控制蜂鸣器。

#ifndef _BEEP_H #define _BEEP_H #include "sys.h" void Beep_Init(void); #endif #include "beep.h" void Beep_Init(void){ //GPIO 避障端口设置 GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4|GPIO_Pin_6; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStructure); //GPIO 蜂鸣器端口设置 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(GPIOA, &GPIO_InitStructure); PAout (0)=1; } if(PAin(4)==0||PAin(6)==0) PAout (0)=0; else PAout (0)=1; 4.main函数 #include "stm32f10x.h" #include "usart.h" #include "delay.h" #include "dianji.h" #include "beep.h" int main(void) { delay_init(); uart_init(9600); Beep_Init(); GPIO_XUJI_Init(); Motor_Init(); while(1) { if(PAin(4)==0||PAin(6)==0) PAout (0)=0; else PAout (0)=1; if((!IN1)&&(!IN3)&&IN2&&IN4){ Motor_SetSpeed1(35); Motor_SetSpeed2(35); } else if(((!IN1)&&(!IN3)&&(!IN4)&&(IN2)) || ((!IN3)&&(!IN4)&&(IN1)&&(IN2))||((!IN3)&&(IN1)&&(IN2)&&(IN4)) || ((!IN4)&&(IN1)&&(IN3)&&(IN2))){ Motor_SetSpeed1(30); Motor_SetSpeed2(-30);//右转 } else if(((!IN1)&&(!IN3)&&(!IN2)&&(IN4)) || ((!IN1)&&(IN3)&&(!IN2)&&(IN4))||((!IN1)&&(IN3)&&(IN2)&&(IN4)) || ((IN1)&&(IN3)&&(!IN2)&&(IN4))){ Motor_SetSpeed1(-30); Motor_SetSpeed2(30);//左转 } else{ Motor_SetSpeed1(0); Motor_SetSpeed2(0); } } } 三,TB6612FNG 驱动模块         TB6612FNG 模块相对于传统的 L298N 效率上提高很多,体积上也大幅度减少,在额定范围内,芯片基本不发热,当然也就显得更加娇贵,所以我们建议有一定动手能力的朋友使用,接线的时候务必 细心细心再细心,注意正负极性。 1.TB6612 的的用法: TB6612 是双驱动,也就是可以驱动两个电机 下面分别是控制两个电机的 IO 口 STBY 口接单片机的 IO 口清零电机全部停止,置 1 通过 AIN1 AIN2,BIN1,BIN2 来控 制正反转 VM 接 12V 以内电源 VCC 接 5V 电源 GND 接电源负极 驱动 1 路 PWMA 接单片机的 PWM 口 真值表: AIN1    0       0         1 AIN2    0       1         0           停止  正传   反转 A01 AO2 接电机 1 的两个脚驱动 2 路 PWMB 接单片机的 PWM 口 真值表: BIN1    0        0       1 BIN2    0        1       0           停止   正传   反转 B01 B02 接电机 2 的两个脚驱动 2 路 2.逻辑真值表 3.占空比 PWM 占空比大小的改变通过对输出比较寄存器 TIMx_ CCR 以及自动重装载寄存器TIMx_ARR 的数值操作来实现,例如当 CCR=203 ,ARR=255 时,占空比为 204/256=80%。编程时将速度变量值写入 CCR寄存器,从而达到改变占空比和对电机调速的目的。 3) 运行性能和建议 1.器件输出状 态在驱动/制动之间切换时,电机转速和 PWM 占空比之 间能保持较好的线性关系,其运行控制效果好于器件在驱动/停止状态之间 切换,所以表 1 中的 INl/IN2 一般不采用 L/L 控制组合。 2.fPWM 较高时,电机运行连续平稳、噪音小,但器件功耗会随频率升 高而增大;fPWM 较低时,利于降低功耗,并能提高调速线性度,但过低的 频率可能导致电机转动连贯性的降低。通常 fPWM>1 kHz 时,器件能够稳定 的控制电机。 3.过大的 PWM 占空比会影响电机驱动电流的稳定性和器件的输出负载 能力,应根据不同的速度要求合理设定占空比范围。 4.器件工作温度过高会导致其输出功率的下降,电路 PCB 设计中应保 证足够面积的覆铜,这样有助于散热,利于器件长时间稳定工作 。 四,结果演示

循迹避障小车



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3