使用STM32智能小车通过蓝牙进行循迹控制

1、功能介绍

蓝牙切换功能:智能小车内置了蓝牙模块,可以通过手机或其他蓝牙设备与之连接。用户可以通过手机发送指令控制小车的运动方向,实现远程控制。

循迹功能:智能小车配备了红外线传感器,可以实现循迹功能。通过检测地面上的黑线或白线,小车能够自动沿着线路行驶,实现自动导航功能。

1.硬件准备

小车底盘一个(两驱),5号4节电池盒一个,STM32f103c8t6最小系统板,红外光电反射传感器两个,ST-LINK下载器,HC-05蓝牙模块、CH340模块、L298N电机驱动模块,焊接设备、一些杜邦线、也可以再准备一个面包板。

硬件搭设

硬件及程序

1.电机驱动

1.主电源正极接12v,主电源负极接GND。

 2.先将5V的跳线帽短接,这样不用额外通过5V输入端外加电源在给单片机供电,可直接有5V输入端连接导线直接给单片机供电,如不将跳线帽短接,则5V输入端输出的电压为12V,连接单片机会导致单片机烧毁

3.A相使能,B相使能是对输入1.2.3.4的控制,如果使能A和使能B加上跳线帽的话,则只需要通过控制输入1.2(一个电机),3.4(另外一个电机)分别给两个电机的两端0和1实现正反转,都给0或者都给1则电机不会转,如果使能A和使能B不加上跳线帽的话,当AB为低电平时,输入1.2.3.4都不会工作,所以可以通过控制使能A和使能B的开和关的周期来控制产生PWM波。
 

2.小车运动

  • 当IN1、IN3为高电平,IN2、IN4为低电平时,电机正转
  • 当IN1、IN3为低电平,IN2、IN4为高电平时,电机反转
  • 都为高电平时,电机不转
  • 电机的正转和反转与跟电机的接线不同而不同,注意自己调试
  • motor.c

    #include "stm32f10x.h"  
    #include "pwm.h"
     
    void motor_init()
    {
    	GPIO_InitTypeDef GPIO_InitStructure;
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    	
    	//2.设置GPIO模式
    	//PB12~PB15 通用推挽输出
     
    	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7 |GPIO_Pin_8 |GPIO_Pin_11;
    	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    	GPIO_Init(GPIOA, &GPIO_InitStructure);
    	  PWM_Init();
    	
    }
     
     
    void car_go()
    {
    		//左轮
    	GPIO_SetBits( GPIOA, GPIO_Pin_6);
    	GPIO_ResetBits( GPIOA,GPIO_Pin_7);
    	TIM_SetCompare3(TIM3, 90  );
     
    	//右轮
    	GPIO_SetBits( GPIOA, GPIO_Pin_8);
    	GPIO_ResetBits( GPIOA,GPIO_Pin_11);
    	TIM_SetCompare4(TIM3, 90  );
    	
    }
    void car_back()
    {
    	//左轮
    	GPIO_ResetBits( GPIOA, GPIO_Pin_6);
    	GPIO_SetBits( GPIOA,GPIO_Pin_7);
    	TIM_SetCompare3(TIM3, 90  );
    	
    	//右轮
    	GPIO_ResetBits( GPIOA, GPIO_Pin_8);
    	GPIO_SetBits( GPIOA,GPIO_Pin_11);
    	TIM_SetCompare4(TIM3, 90  );
     
    }
    void car_right()
    {
    	//左轮
    	GPIO_ResetBits( GPIOA, GPIO_Pin_6);
    	GPIO_ResetBits( GPIOA,GPIO_Pin_7);
    		TIM_SetCompare3(TIM3, 90  );
    	
    	//右轮
    	GPIO_SetBits( GPIOA, GPIO_Pin_8);
    	GPIO_ResetBits( GPIOA,GPIO_Pin_11);
    	TIM_SetCompare4(TIM3, 75);
    	
    }
    void car_left ()
    {
    	//左轮
    	GPIO_SetBits( GPIOA, GPIO_Pin_6);
    	GPIO_ResetBits( GPIOA,GPIO_Pin_7);
    	TIM_SetCompare3(TIM3, 75 );
     
    	//右轮
    	GPIO_ResetBits( GPIOA, GPIO_Pin_8);
    	GPIO_ResetBits( GPIOA,GPIO_Pin_11);
    	TIM_SetCompare4(TIM3, 90 );
     
    }
    void car_stop ()
    {
    	//左轮
    	GPIO_ResetBits( GPIOA, GPIO_Pin_6);
    	GPIO_ResetBits( GPIOA,GPIO_Pin_7);
     
    	//右轮
    	GPIO_ResetBits( GPIOA, GPIO_Pin_8);
    	GPIO_ResetBits( GPIOA,GPIO_Pin_11);
     
    }
    
    

    pwm.c

    #include "stm32f10x.h"
     
    void PWM_Init(void)
    {
    	
    	
    	GPIO_InitTypeDef GPIO_InitStructure;
    	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
    	TIM_OCInitTypeDef TIM_OCInitStructure;
    	//1.打开时钟
      RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    	
    	//PB8,PB9 复用推挽输出
    	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
    	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    	GPIO_Init(GPIOB, &GPIO_InitStructure);
    	
    	//初始化TIM4 100us
    	TIM_InternalClockConfig(TIM3);
    	
    	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(TIM3, &TIM_TimeBaseInitStructure);
    	
    	//初始化PWM波形
    	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_OC3Init(TIM3, &TIM_OCInitStructure);//初始化右轮
    	
    	
    	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(TIM3, &TIM_TimeBaseInitStructure);
     
     
    	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_OC4Init(TIM3, &TIM_OCInitStructure);//初始化左轮
    	
    	//使能定时器
    	TIM_Cmd(TIM3, ENABLE);
     
    }
     
    void PWM_SetCompare3(uint16_t Compare)
    {
    	TIM_SetCompare3(TIM3, Compare);
    }
    void PWM_SetCompare4(uint16_t Compare)
    {
    	TIM_SetCompare3(TIM3, Compare);
    }
    

    2.循迹模块

    循迹模块通常具有两个红外传感器,可以通过连接线将其与单片机的GPIO口相连。确保连接正确且稳固。首先,初始化单片机的相关引脚,并设置为输入模式。然后,循迹模块的红外传感器将会输出高低电平信号,根据这些信号判断当前位置是否在黑线上。可以使用if语句或逻辑判断来处理不同的情况,例如当传感器检测到黑线时小车继续前进,当传感器检测到白线时小车停止或转向等。

    track.c

    
    #include "stm32f10x.h"
    #include "motor.h"
     
    #define track_left  GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_6)
    #define track_right  GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_8)
     
    void track_Init(){
    	GPIO_InitTypeDef GPIO_InitStructure;
     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    	
    	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_8;
    	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    	GPIO_Init(GPIOB, &GPIO_InitStructure);
     
    }
     
    void track(){
     if(track_left==0 && track_right==0 ){
       car_go();
        }
     else if(track_left==1 && track_right==0){
      car_left();
      }
     else if(track_left==0 && track_right==1){
     
     car_right();
      }
     else {
      car_stop();
      }
     
     
    }	
    
    

    3.蓝牙模块

    蓝牙模块的前期调试,可用usb转ttl模块连接蓝牙模块,RXD-TX TXD-RX VCC-VCC GND-GND。

    如果上电了,蓝牙指示灯默认是2s闪烁就是进入了AT指令模式,可通过上位机向蓝牙发送指令。如果上电不是AT指令模式,就摁着蓝牙的按键再上电。

    AT指令集(建议改名字就好,密码不要改)

    AT+NAME=Bluetooth-Master  蓝牙主机名称为Bluetooth-Master

    AT+ROLE=1                蓝牙模式为主模式

    AT+CMODE=0               蓝牙连接模式为任意地址连接模式

    AT+PSWD=1234             蓝牙配对密码为1234

    AT+UART=9600,0,0       蓝牙通信串口波特率为9600,停止位1位,无校验位

    AT+RMAAD                 清空配对列表

    usart.c

    #include "stm32f10x.h"
    #include "usart.h"	
    #include "pwm.h"
    #include "motor.h"
    #include "track.h"
     
    //
    //加入以下代码,支持printf函数,而不需要选择use MicroLIB	  
    #if 1
    #pragma import(__use_no_semihosting)             
    //标准库需要的支持函数                 
    struct __FILE 
    { 
    	int handle; 
     
    }; 
     
    FILE __stdout;       
    //定义_sys_exit()以避免使用半主机模式    
    void _sys_exit(int x) 
    { 
    	x = x; 
    } 
    //重定义fputc函数 
    int fputc(int ch, FILE *f)
    {      
    	while((USART1->SR&0X40)==0);//循环发送,直到发送完毕   
        USART1->DR = (u8) ch;      
    	return ch;
    }
    #endif 
     
     
     
    #if EN_USART1_RX   //如果使能了接收
    //串口1中断服务程序
    //注意,读取USARTx->SR能避免莫名其妙的错误   	
    u8 USART_RX_BUF[USART_REC_LEN];     //接收缓冲,最大USART_REC_LEN个字节.
    //接收状态
    //bit15,	接收完成标志
    //bit14,	接收到0x0d
    //bit13~0,	接收到的有效字节数目
    u16 USART_RX_STA=0;       //接收状态标记	  
      
    void usart_Init(void)
    {
    	
      //GPIO端口设置
      GPIO_InitTypeDef GPIO_InitStructure;
    	USART_InitTypeDef USART_InitStructure;
    	NVIC_InitTypeDef NVIC_InitStructure;
    	 
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_USART1, 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 = 9600;//串口波特率
    	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);               
    }
     
     
     
    #endif	
     
    void USART1_IRQHandler(void)
    {
    	int res;
    	if(USART_GetITStatus( USART1, USART_IT_RXNE)==SET)
    	
    	res=USART_ReceiveData(USART1);
    	switch(res){
    		
    		case '1': car_go();break;	
    		case '2': car_back();break;
    		case '3': car_left();break;
    		case '4': car_right();break;
    		case '5': car_stop();break;
     
    	USART_ClearITPendingBit( USART1, USART_IT_RXNE);
    	}
    }
    
    

    usart.h

    #ifndef __USART_H
    #define __USART_H
    #include "stdio.h"	
    #include "stm32f10x.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;         		//接收状态标记	
    //如果想串口中断接收,请不要注释以下宏定义
    void uart_init();
    #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);
    	}
    }
    

    main.c

    #include "stm32f10x.h"
    #include "Delay.h"
    #include "motor.h"
    #include "pwm.h"
    #include "track.h"
    #include "usart.h"
     
    #define key  GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_3)
     
     
    uint8_t Speed;
     
    void delay_s(int s);
    void USART1_IRQHandler(void);
    void usart_Init();
     
    void delay_s(int s)
    {
    	uint8_t b;
    	for( b = s;b>0;b--)
    	{
    		delay_ms(1000);
    	}
    }
     
     
    int main(void) 
    {
    	
    	motor_init();
      usart_Init();
    	track_Init();
     
    	while(1)
    	{   
    		if(key==0)
    		{
      	USART1_IRQHandler();
    		}
    		else
    		{
    		track();
    		}
       }
    }
    
    
    

     

    物联沃分享整理
    物联沃-IOTWORD物联网 » 使用STM32智能小车通过蓝牙进行循迹控制

    发表评论