2022暑期学校实战:简单实现2021年电子设计竞赛国赛题目

目录

设计目标:

设计思路:

设计方案:

电机驱动

1)时钟配置

2)SYS配置

3)PWM信号输出

4)电机控制思路

5)电机驱动代码

电脑通讯

 1)CubeMx配置

 2)电脑通讯代码

HC05蓝牙通讯

1)HC05原理讲解

2)HC05蓝牙通讯代码

OpenMv处理

1)OpenMv道路识别

2)OpenMv的串口通讯

设计问题及解决:

1、STM32F429只引出了USART1引脚。

2、蓝牙模块连接的时候有效距离很短。


设计目标:

项目在2021年电子设计竞赛的国赛题目上进行了一些改动、

基本功能:其需要运输物品到指定位置(6个虚线位置),运输完成后将回到起始点。

新加功能:通过从另一片MCU远程提供目标点位置,在车上放物品后可以自动前往目标位置,同时远程MCU可以显示小车运动轨迹。

设计思路:

设计分为两块,分别为:

1、STM32F103:图形界面显示地图信息、触摸屏提供目标位置、HC05蓝牙通讯向小车传输目标点位置。

2、STM32F429:控制两类电机驱动L298N、AT8236、HC05蓝牙通讯接收目标点位置、OpenMv H7用于识别道路中的特殊位置如:十字路口、丁字路口等。

设计方案:

  • 电机驱动

  • 为了保证小车速度,四轮均单独使用一对PWM信号,故采用两个驱动,驱动应尽量一致,但由于实验中有一驱动烧毁故采用不同的驱动实现。

    小车电机型号为:

    虽然AT8236拥有编码器控制模块,但电机并非编码电机,估值采用四路信号输出PWM信号控制电机转动。

    从IN1、IN2、IN3、IN4分别与STM32F429的四个GPIO口相连,输出PWM信号,从而控制电机正反转及转动速度。使用STM32CubeMx配置GPIO口输出PWM信号,具体操作如下:

    1)时钟配置

    1. 配置RCC中高速时钟HSE使用Crystal/Ceramic Resonator

            b)时钟树配置

    2)SYS配置

    其中Debug必须选用Serial Wire,否则会有板子下载后将无法找到板子从而无法再次下载的问题。

    从OUT1、OUT2、OUT3、OUT4分别与小车电机的红黑线相连,从而保证小车可以正转,也可以反转。

    3)PWM信号输出

    采用TIM4和TIM5输出八路PWM信号,其中频率为:

    96MHz/(95+1)/(254+1)=3.9KHZ,初始化占空比设置为0。

    4)电机控制思路

     电机控制的总体流程如下

    电机控制的具体流程为:

     在电机控制存在一个死循环,在死循环中不断地接收OpenMv的道路信息,更新车轮转速,进而达到寻线的目的,其中采用PID算法控制电机转速,由于题目中的路段基本都为直线和一些特殊交叉道路,故项目使用固定转弯时间来控制器转弯,转弯后继续寻线。 

    5)电机驱动代码

    此时根据CubeMx生成代码加入motor.c和motor.h文件,用于控制小车电机。部分代码如下:

    //motor.h
    void control_motor(int pwm_left, int pwm_right);
    void stop_car();
    void track_and_run();
    void trun_left();
    void trun_right();
    void go();
    void turn_180();
    void go_home(int state);
    void control_motor_all(int pwm_leftf, int pwm_leftr,int pwm_rightf,int pwm_rightr);
    void run_way1(int state);
    void run_way2(int state);
    void run_way3(int state);
    void run_way4(int state);
    void run_way5(int state);
    void run_way6(int state);
    //motor.c
    //定义输出的PWM输出通道
    #define MOTOR_L &htim5
    #define MOTOR_LF_F TIM_CHANNEL_4
    #define MOTOR_LF_B TIM_CHANNEL_3
    #define MOTOR_LR_F TIM_CHANNEL_2
    #define MOTOR_LR_B TIM_CHANNEL_1
    #define MOTOR_R &htim4
    #define MOTOR_RF_F TIM_CHANNEL_4
    #define MOTOR_RF_B TIM_CHANNEL_3
    #define MOTOR_RR_F TIM_CHANNEL_2
    #define MOTOR_RR_B TIM_CHANNEL_1
    
    //定义PWM信号的最大最小值
    #define pwm_max 255
    #define pwm_min -255
    #include "motor.h"
    #include "tim.h"
    
    int value=0;//表示从Openmv得到的路况信息
    int turn_now=0;//无用
    int stop_car_flag=0;//1表示停车
    int stop_state=0;//停止线的数目
    int T_num[4] = {0,0,0,0};//十字、T型、左上、右上
    uint8_t PIC_buffer=1;//缓冲区,无用
    extern int Move_modle;
    extern int YL_start;
    //int a=0;
    
    //电机四轮驱动函数
    void control_motor_all(int pwm_leftf, int pwm_leftr,int pwm_rightf,int pwm_rightr)
    {
      if (pwm_leftf > 0) {
          __HAL_TIM_SetCompare(MOTOR_L,MOTOR_LF_F, pwm_leftf);
          __HAL_TIM_SetCompare(MOTOR_L,MOTOR_LR_F, pwm_leftr);
          __HAL_TIM_SetCompare(MOTOR_L,MOTOR_LF_B, 0);
          __HAL_TIM_SetCompare(MOTOR_L,MOTOR_LR_B, 0);
        }
        else {
          __HAL_TIM_SetCompare(MOTOR_L,MOTOR_LF_F, 0);
          __HAL_TIM_SetCompare(MOTOR_L,MOTOR_LR_F, 0);
          __HAL_TIM_SetCompare(MOTOR_L,MOTOR_LF_B, -pwm_leftf);
          __HAL_TIM_SetCompare(MOTOR_L,MOTOR_LR_B, -pwm_leftr);
        }
        if (pwm_rightf > 0) {
          __HAL_TIM_SetCompare(MOTOR_R,MOTOR_RF_F, pwm_rightf);
          __HAL_TIM_SetCompare(MOTOR_R,MOTOR_RR_F, pwm_rightr);
          __HAL_TIM_SetCompare(MOTOR_R,MOTOR_RF_B, 0);
          __HAL_TIM_SetCompare(MOTOR_R,MOTOR_RR_B, 0);
        }
        else {
          __HAL_TIM_SetCompare(MOTOR_R,MOTOR_RF_F, 0);
          __HAL_TIM_SetCompare(MOTOR_R,MOTOR_RR_F, 0);
          __HAL_TIM_SetCompare(MOTOR_R,MOTOR_RF_B, -pwm_rightf);
          __HAL_TIM_SetCompare(MOTOR_R,MOTOR_RR_B, -pwm_rightr);
        }
      
    }
    //停车
    void stop_car()
    {
      __HAL_TIM_SetCompare(MOTOR_L,MOTOR_LF_F, 0);
      __HAL_TIM_SetCompare(MOTOR_L,MOTOR_LR_F, 0);
      __HAL_TIM_SetCompare(MOTOR_L,MOTOR_LF_B, 0);
      __HAL_TIM_SetCompare(MOTOR_L,MOTOR_LR_B, 0);
      __HAL_TIM_SetCompare(MOTOR_R,MOTOR_RF_F, 0);
      __HAL_TIM_SetCompare(MOTOR_R,MOTOR_RR_F, 0);
      __HAL_TIM_SetCompare(MOTOR_R,MOTOR_RF_B, 0);
      __HAL_TIM_SetCompare(MOTOR_R,MOTOR_RR_B, 0);
    }
    //检测PWM信号是否在给定范围之内
    int check_pwm(int value)
    {
    	if(value>pwm_max)return pwm_max;
    	if(value<pwm_min)return pwm_min;
    	return value;
    }
    
    //主控函数
    void track_and_run(int state) {
    	if(Move_modle==1)
    	{
    		run_way1(state);
    	}
    	else if(Move_modle==2)
    	{
    		run_way2(state);
    	}
    	else if(Move_modle==3)
    	{
    		run_way3(state);
    	}
    	else if(Move_modle==4)
    	{
    		run_way4(state);
    	}
    	else if(Move_modle==5)
    	{
    		run_way5(state);
    	}
    	else if(Move_modle==6)
    	{
    		run_way6(state);
    	}
    	
    }
    
    
    void trun_right()
    {
    	HAL_Delay(160);
    	turn_now=1;
    	control_motor_all(235,200,-215 ,-200);
    	HAL_Delay(240);
    }
    
    
    void go()
    {
    	//int tracker_offset=-340;
    			float tracker_KP;
    			int tracker_val;
    			float left_mult,right_mult;
    			int pwm_base = 80;
    			int pwm_left, pwm_right;
    			//tracker_val = track_degrees;
    			tracker_val = value;
    			left_mult=1.0;
    			right_mult=0.7;
    			tracker_KP=4;
    
    			
    			pwm_left = (int)(pwm_base - tracker_val * tracker_KP*left_mult);
    			pwm_left = check_pwm(pwm_left);
    			pwm_right = (int)(pwm_base + tracker_val * tracker_KP*right_mult );
    			pwm_right = check_pwm(pwm_right);
    
    			control_motor(pwm_left,pwm_right);
    }
    
    
    
    void run_way1(int state)
    {
    	
    	if (stop_car_flag==0 &&YL_start==1)//去的走法
    	{
    		if (state==0)
    		{
    			go();
    		}
    		else if(state==1)
    		{
    			//printf("%d\n%d\n%d\n",0xf1,1,0xf3);
    			trun_left();
    		}
    		else if(state==2 )
    		{
    			stop_car();
    		}
    		else if(state==3 )//左边上边
    		{
    			stop_car();
    		}
    		else if(state==4 )//右边上边
    		{
    			stop_car();
    		}
    	}
    	else if (stop_car_flag==1 &&YL_start==2)//回的走法
    	{
    		if (state==0)
    		{
    			go();
    		}
    		else if(state==1)
    		{
    			//printf("%d\n%d\n%d\n",0xf1,1,0xf3);
    			trun_right();
    		}
    		else if(state==2 )
    		{
    			stop_car();
    		}
    		else if(state==3 )//左边上边
    		{
    			stop_car();
    		}
    		else if(state==4 )//右边上边
    		{
    			stop_car();
    		}
    	}
    	else //回来后停车
    	{
    		stop_car();
    	}
    }
    
    

  • 电脑通讯

  •  1)CubeMx配置

    STM32F429中使用USART3实现,原理图如下:

    只需要在CubeMx中打开串口模式为Asynchronous即可完成串口调用

     2)电脑通讯代码

     USART的代码由CubeMx自动生成,其中需要自己添加重定向函数,并将Keil中魔术棒中Target一栏下Use MicroLIB勾选,即可通过printf()函数从串口输出数据。

     由于使用的STM32F429只引出了USART1的RX和TX,故对之后的串口通讯也从USART1的RX和TX输入输出数据,故在此声明了回调函数收集OpenMv的数据。printf可以将数据从TX输出,USART1_RXbuff接受了RX传输的数据。

    /* USER CODE BEGIN 0 */
    int fputc(int ch ,FILE *f)//重定向
    {
    	
    	HAL_UART_Transmit (&huart1 ,(uint8_t *)&ch , 1,HAL_MAX_DELAY );
    	return ch ;
    }
    
    //UART
    void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
    {
      uint16_t tempt;
      if(huart->Instance==USART1)
      {
        tempt=USART1_RXbuff;
        Openmv_Receive_Data(tempt);
    	HAL_UART_Receive_IT(&huart1,(void *)&USART1_RXbuff,1);
      }	
    
    }
    
    /* USER CODE END 0 */
  • HC05蓝牙通讯

  • 1)HC05原理讲解

    HC05是一种比较集成的蓝牙模块,我们只需要调用串口就可以完成其传输功能,但必须将HC05进入AT模式调节对应参数。

    1. 长按按钮后接通VCC、GND、RXD、TXD即可进入AT模式,此时通过串口助手发送“AT+回车“,收到ok返回即进入AT模式。
    2. 在AT模式中主要设置连接密码,主从机地址等。具体参考文章:https://blog.csdn.net/seek97/article/details/81333701
    3. 拔掉电源后重新插入即可进入正常工作模式,此时会和设置好的蓝牙地址自动进行连接。
    4. 真正实现通讯还需要打开单片机的USART,通过RXD和TXD收发数据,此过程即简单的串口通讯过程,与电脑通讯类似,不再赘述。

    2)HC05蓝牙通讯代码

     此部分代码和电脑端通讯代码是一样的,printf可以将数据从TX输出,USART1_RXbuff接受了RX传输的数据。

    /* USER CODE BEGIN 0 */
    int fputc(int ch ,FILE *f)//重定向
    {
    	
    	HAL_UART_Transmit (&huart1 ,(uint8_t *)&ch , 1,HAL_MAX_DELAY );
    	return ch ;
    }
    
    //UART
    void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
    {
      uint16_t tempt;
      if(huart->Instance==USART1)
      {
        tempt=USART1_RXbuff;
        Openmv_Receive_Data(tempt);
    	HAL_UART_Receive_IT(&huart1,(void *)&USART1_RXbuff,1);
      }	
    
    }
  • OpenMv处理

  • 1)OpenMv道路识别

    以往小车识别道路需要识别黑色路线,而2021年国赛题目中需要识别红色道路,这要求OpenMv的图片格式必须为RGB565格式,同时在识别道路的时候采用阈值排除黑色边框的干扰。

    2)OpenMv的串口通讯

     OpenMv的串口通讯拥有现成的函数,项目将其封装处理后可以通过帧头+内容+帧尾的形式收发数据。

    #串口输出函数
    def sending_data(Data1):
        global uart;
        #frame=[0x2C,18,cx%0xff,int(cx/0xff),cy%0xff,int(cy/0xff),0x5B];
        #data = bytearray(frame)
        data = ustruct.pack("<bbbb",      #格式为俩个字符俩个短整型(2字节)
                       0xF0,
                       0xF2,
                       Data1,
                       0xFF
                       )
        uart.write(data);   #必须要传入一个字节数组
    
    #串口输入函数
    def receive_data(Data):
        global LYstate
        global Move_model
        global Move_model_
        if Data !=' ':#可能有一些异常数据
            if LYstate==0 and Data=='241':#帧头
               LYstate=1
            elif LYstate==1:
                if Data=='243':#帧尾
                    LYstate=0
                else:
                    Move_model_=Data
                    LYstate=2
            elif LYstate==2 and Data=='243':
                    Move_model=Move_model_
                    LYstate=0
            else:
                LYstate=0
    

    OpenMv和单片机的通讯仍然只需要采用RXD和TXD传输数据即可。

    设计问题及解决:

    1、STM32F429只引出了USART1引脚。

    从OpenMv的TXD给STM32F429的RXD传输数据,这部分用于识别道路并控制电机转动。

    从STM32F429的TXD通过蓝牙模块给STM32F103的RXD传输数据,这部分将小车目前状况放回到操纵者手中。

    从STM32F103的TXD通过蓝牙模块给OpenMv的RXD传输数据,这部分用于远程遥控小车系统。

    2、蓝牙模块连接的时候有效距离很短。

            发现是VCC输入3.3V导致,输入5V的VCC可以正常工作。

    物联沃分享整理
    物联沃-IOTWORD物联网 » 2022暑期学校实战:简单实现2021年电子设计竞赛国赛题目

    发表评论