精华内容
下载资源
问答
  • 基于STM32的智能车,利用超声波和循迹模块实现避障以及超声波检测的功能。
  • 基于STM32C8T6的超声波避障小车,功能描述:检测到前方有障碍物,后退一下,然后左转90度直行离开。另外里面还附赠循迹函数(写了但是没调用),循迹函数可以实现的功能:虚线转弯,直角弯,弧形弯,冲坡,十字路口...
  • 能够自主检测前方距离,小于一定值时,舵机带动超声波测距传感器分别向左、向右转向,分别测距并比较,控制小车向距离较大侧转弯;如果两侧距离均小于设定值,控制小车后退,再分别检测左右距离
  • 智能小车使用红外探头避障,适应不同颜色标志
  • 资源包括:程序,程序设计报告 ...思路:通过驱动超声波模块,测量出小车与障碍物之间的距离,并通过蓝牙反馈数据到手机,同时根据测量数据进行舵机的转动控制,从而实现智能避障(建议先看程序设计报告总结部分)
  • 【项目分享】基于STM32超声波避障小车

    千次阅读 多人点赞 2020-01-21 16:10:45
    不管是对于初学者还是对于一个玩过单片机的电子爱好者来说,或多或少都接触到过小车项目,今天给大家介绍的的一个项目基于STM32超声波避障小车。这也是我曾经的一个课设,在此开源分享给大家,全文5000多字,干货满...

    微信公众号:果果小师弟
    关注可了解获取更多资料教程。问题或建议,请公众号留言;
    如果你觉得此篇文章对你有帮助,欢迎留言关注

    不管是对于初学者还是对于一个玩过单片机的电子爱好者来说,或多或少都接触到过小车项目,今天给大家介绍的的一个项目基于STM32超声波避障小车。这也是我曾经的一个课设,在此开源分享给大家,全文5000多字,干货满满,加油读完,保证你收货多多。

    震惊!某大学生竟然在宿舍做stm32避障小车

    处理器电路设计

    单片机是系统的CPU,是系统稳定、正常运行的重要前提,以下为单片机选型的两种方案:
    (1)传统的8位单片机,是通过超大规模集成电路对其进行集成为一个独立芯片的控制器。内部组件包括CPU、随机存储器、只读存储器、I/O接口、中断系统、计时器、串口通讯、数模转换等。STC89C52单片机是最常见的51单片机,但是资源较少,精确度低,处理速度相比STM32单片机差很多。
    (2)使用目前市面上最常见的STM32单片机,STM32系列单片机可分为ARMCortex-M3内核体系结构的不同应用领域。它可分为STM32F1系列和STM32F4系列,STM32F1系列单片机时钟频率最高可达72米,在同一产品中性能最好。单片机的基本处理速度为36米,16位单片机的性能也很好。微晶片的内建快闪记忆体相当大,范围从32kb到512kb,可视需要选择。单个设备的功耗非常低,仅360mA,32位单片机产品的功耗最低,每兆赫只有0.5安培。特别值得一提的是,内接单晶片汇流排是一种Harvard架构,可执行速度高达1.25 DMIPS/MHz的指令。此芯片越来越多地被用作主要控制器。
    通过对单片机的资源和处理时间的速度我们采用选择STM32103C8T6为本系统主控芯片,程序下载是只需要一个JLINK就可以轻松完成。控制器模块电路如下所示:
    单片机最小系统

    电源模块设计

    本设计采用锂电池供电, 模块的供电电压一般都为5V,同时超声波模块需要较大的电流才能正常工作,所以在降压的基础上也要保证足够大的输出电流。本设计采用可调输出版本,模块的输入电压范围广,输出电压在1.25V-35V内可调,电压转换效率高,输出纹波小。降压电路如下所示:

    电源管理电路

    电机驱动模块设计

    要完成转向是能够利用单片机实现的,然而单片机I0的带负载能力弱,因此我们选择了大功率放大器件TB6612FNG。TB6612FNG是采用MOSFET-H桥结构的双通道大电流电路输出,可以控制2个电机的驱动。相比普通的电机驱动,外围电路非常简单,只需要一个芯片和一个钽电容进行PWM输出滤波,系统尺寸小。PWM信号输入频率范围广,轻松满足本设计的需求。

    电源驱动芯片

    电源驱动引脚表

    控制芯片:TB6612
    控制芯片数量:2
        1号TB6612引脚分配:
         VM         PWMA--------->TIM1_CH1(PA8)
         VCC        AIN2--------->GPIOB_12
         GND        AIN1--------->GPIOB_13
         AO1        STBY--------->GPIOB_14
         AO2        BIN1--------->GPIOB_15
         BO2        BIN2--------->GPIOA_12
         BO1        PWMB--------->TIM1_CH2(PA9)
         GND        GND
        2号TB6612引脚分配:
         VM         PWMA--------->TIM1_CH3(PA10)
         VCC        AIN2--------->GPIOB_5
         GND        AIN1--------->GPIOB_6
         AO1        STBY--------->GPIOB_7
         AO2        BIN1--------->GPIOB_8
         BO2        BIN2--------->GPIOA_9
         BO1        PWMB--------->TIM1_CH4(PA11)
         GND        GND
    真值表
         AIN1   0     1     0     1
         AIN2   0     0     1     1
         BIN1   0     1     0     1
         BIN2   0     0     1     1
               停止  正转  反转  刹车
    

    电机所用到的定时器配置

    //初始化TIMX,设置TIMx的ARR,PSC
    //arr:自动重装载初值,psc为预分频值,两者配合控制定时器时钟的周期
    //定时器选择TIM1
    static void TB6612_ADVANCE_TIM1_Mode_Config(TIM_TypeDef* TIMx,uint16_t arr,uint16_t psc,uint16_t duty)
     {
         //-----------------时基结构体初始化-------------------------/
         TIM_TimeBaseInitTypeDef TIM_TimeStructure;
         /*开启定时器1时钟,即内部时钟CK_INT=72M*/
         RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE);
        TIM_DeInit(TIMx);
        /*内部时钟作为计数器时钟,72MHZ*/
        TIM_InternalClockConfig(TIMx);
        /*自动重装载寄存器的值,累计TIM_Period+1个频率后产生一个更新或者中断*/
        TIM_TimeStructure.TIM_Period=arr;
        /*时钟预分频系数为71,则驱动计数器的时钟CK_CNT=CK_INT/(71+1)=1MHZ*/
        TIM_TimeStructure.TIM_Prescaler=psc-1;
        /*设置时钟分割,TIM_CKD_DIV1=0,PWM波不延时*/
        TIM_TimeStructure.TIM_ClockDivision=TIM_CKD_DIV1;
        /*向上计数模式*/
        TIM_TimeStructure.TIM_CounterMode=TIM_CounterMode_Up;
        /*重复计数器*/
        TIM_TimeStructure.TIM_RepetitionCounter=0;
        /*初始化定时器*/
        TIM_TimeBaseInit(TIMx,&TIM_TimeStructure);
        /*使能ARR预装载寄存器(影子寄存器)*/
        TIM_ARRPreloadConfig(TIMx,ENABLE);
        //-----------------输出比较结构体初始化-----------------------/
        TIM_OCInitTypeDef   TIM_OCInitStructure;
        /*PWM模式设置,设置为PWM模式1*/
        TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1;
        /*PWM输出使能相应的IO口输出信号*/
        TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;
        /*设置占空比大小,CCR1[15:0]: 捕获/比较通道1的值,若CC1通道配置为输出:CCR1包含了装入当前捕获/比较1寄存器的值(预装载值)。*/
        TIM_OCInitStructure.TIM_Pulse=duty;
        /*输出通道电平极性设置*/
        TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_High;
        /*初始化输出比较参数*/
        TIM_OC1Init(TIMx,&TIM_OCInitStructure);//初始化TIM1 通道1
        TIM_OC2Init(TIMx,&TIM_OCInitStructure);//初始化TIM1 通道2
        TIM_OC3Init(TIMx,&TIM_OCInitStructure);//初始化TIM1 通道3
        TIM_OC4Init(TIMx,&TIM_OCInitStructure);//初始化TIM1 通道4
        /*自动重装载*/
        TIM_OC1PreloadConfig(TIMx,TIM_OCPreload_Enable);
        TIM_OC2PreloadConfig(TIMx,TIM_OCPreload_Enable);
        TIM_OC3PreloadConfig(TIMx,TIM_OCPreload_Enable);
        TIM_OC4PreloadConfig(TIMx,TIM_OCPreload_Enable);
        /*使能计数器*/
        TIM_Cmd(TIMx,ENABLE);
        /*主输出使能,如果设置了相应的使能位(TIMx_CCER寄存器的CCxE、CCxNE位),则开启OC和OCN输出。*/
        TIM_CtrlPWMOutputs(TIMx,ENABLE);    
    }
    //高级定时器输出通道初始化函数
    static void TB6612_ADVANCE_TIM_Gpio_Config()
    {
        GPIO_InitTypeDef  GPIO_InitStruct;
        /*----------通道1配置--------------*/
        /*定时器1输出比较通道*/
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
        /*配置为复用推挽输出*/
        GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;
        GPIO_InitStruct.GPIO_Pin=GPIO_Pin_8;
        GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
        GPIO_Init(GPIOA,&GPIO_InitStruct);
        /*-----------通道二配置-------------*/
        /*定时器1输出比较通道*/
        /*配置为复用推挽输出*/
        GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;
        GPIO_InitStruct.GPIO_Pin=GPIO_Pin_11;
        GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
        GPIO_Init(GPIOA,&GPIO_InitStruct);
         /*-----------通道三配置-------------*/
        /*定时器1输出比较通道*/
        /*配置为复用推挽输出*/
        GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;
        GPIO_InitStruct.GPIO_Pin=GPIO_Pin_9;
        GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
        GPIO_Init(GPIOA,&GPIO_InitStruct);
         /*-----------通道四配置-------------*/
        /*定时器1输出比较通道*/
        /*配置为复用推挽输出*/
        GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;
        GPIO_InitStruct.GPIO_Pin=GPIO_Pin_10;
        GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
        GPIO_Init(GPIOA,&GPIO_InitStruct);
    }
    

    超声波模块

    采用HC-SR04超声波模块,该芯片具有较高的集成度以及良好的稳定性,测度距离十分精确,十分稳定。供电电压为DC5V供电电流小于10mA,探测距离为0.010m-3.5m一共有四个引脚VCC(DC5V)、Triger(发射端)、Echo(接收端)、GND(地)。HC-SR04实物图如下:

    超声波实物
    该模块是利用单片机的IO触发电平测距,单片机内部利用普通定时器产生一个高电平信号之后,超声波就可以自主发送频率为40khz的方波,然后等待信号的返回;若有信号返回,单片机IO口就立刻输出一高电平,利用高电平产生的时间可以计算小车与障碍物的距离。最终距离就是高电平持续时间乘以声音在空气中传播的速度再除以2,可以反复测量距离。
    在程序开始首先初始化超声波,利用定时器并设置时基的自动重装载初值1000,psc为预分频值72,这样的话我们产生一次中断的时间是1ms,并设置抢占优先级0,子优先级3。HC_SR04_Echo引脚接收到高电平,打开定时器,且每1ms进入一次中断。在测量时首先让Trig发送一个大于10us的高电平,然后拉高HC_SR04_Trig,当Echo为0时打开定时器计时,当Echo为1时关闭定时器,通过公式计算距离。
    模块工作原理:
    (1)单片机触发引脚,输出高电平信号;
    (2)模块发送端自动发送特定频率的方波;
    (3)如果有信号返回,通过IO输出一高电平,高电平持续的时间就是超声波的发射时长;
    (4)测试距离=(高电平时间*声速(340M/S))/2。
    注意:在硬件操作上需要首先让模块地端先连接,否则会影响模块工作。测距时,被测物体的摆放不能太过于杂乱,否则会影响测试结果。

    超声波重要代码(可参考)

    /* 获取接收到的高电平的时间(us*/
     uint32_t Get_HC_SR04_Time(void)
     {
         uint32_t t=0;
         t=Acoustic_Distance_Count*1000;//us
         t+=TIM_GetCounter(TIM2);//获取us
         TIM2->CNT =0;
         Acoustic_Distance_Count=0;
         Systic_Delay_us(100);
        return t;
    }
    /*获取距离*/
    void Get_HC_SR04_Distance(void)
    {
        static uint16_t count=0;
        switch(count)
        {
            case 1:
            {
                GPIO_SetBits(Acoustic_Port,HC_SR04_Trig);//Trig发送一个大于10us的高电平
            }break;
    
            case 15:
            {
                count=0;
                GPIO_ResetBits(Acoustic_Port,HC_SR04_Trig);
                while(GPIO_ReadInputDataBit(Acoustic_Port,HC_SR04_Echo)==0);//当Echo为0时打开定时器 计时
                Open_Tim2();
                while(GPIO_ReadInputDataBit(Acoustic_Port,HC_SR04_Echo)==1);//当Echo为0时打开定时器 计时
                Close_Tim2();
                HC_SR04_Distance=(float)(Get_HC_SR04_Time()/5.78);
    
            }break;
            default:break;
        }
        count++;
    }
    

    舵机模块

    本系统使用的是SG90型号的舵机,舵机是一种常见的角度驱动器,本系统需要判断不同位置的障碍物可以且对转向的力度小。舵机可以理解为方向盘称,方向盘是一个常见的名字。它实际上是一个伺服马达。舵机实物图如下:

    舵机模块接口简单,舵机模块只有三个引脚。分别引引出了三根线左右两边是电源正负接口线,中间一根是PWM信号线直接连接单片机的控制引脚。通过控制单片机的引脚输出的脉冲宽度进而控制舵机旋转的角度。舵机每增加0.1ms 舵机对应增加9度。
    0.5ms---------0
    1.0ms---------45
    1.5ms---------90
    2.0ms---------135
    2.5ms-----------180
    20ms的时基脉冲,如果想让舵机转90度,就应该发生一个高电平持续时间为1.5ms,周期为20ms的方波,duty=1.5/20=7.5%。在这里设置定时器自动重装载寄存器arr的值为1000,所以当占空比为百分之75是,在程序中就要设置占空比为75/1000=7.5%, 这就是具体的算法。

    舵机重要代码(可参考)

    /**PWM引脚初始化*/
     static void SERVO_Gpio_Init(void)
     {
         GPIO_InitTypeDef  GPIO_InitStruct;
         /*----------通道2配置--------------*/
         /*定时器3输出比较通道*/
         RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
         /*配置为复用推挽输出*/
         GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;
        GPIO_InitStruct.GPIO_Pin=GPIO_Pin_7;
        GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
        GPIO_Init(GPIOA,&GPIO_InitStruct); 
    }
    //定时器3初始化,设置TIMx的ARR,PSC
    //arr:自动重装载初值,psc为预分频值,两者配合控制定时器时钟的周期
    static void SERVO_TIM_Config(TIM_TypeDef* TIMx,uint16_t arr,uint16_t psc,uint16_t duty)
    {
        //-----------------时基结构体初始化-------------------------/
        TIM_TimeBaseInitTypeDef TIM_TimeStructure;
        /*开启定时器3时钟,即内部时钟CK_INT=72M*/
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
        TIM_DeInit(TIMx);
        /*内部时钟作为计数器时钟,72MHZ*/
        TIM_InternalClockConfig(TIMx);
        /*自动重装载寄存器的值,累计TIM_Period+1个频率后产生一个更新或者中断*/
        TIM_TimeStructure.TIM_Period=arr;//1000 当定时器从0计数到999,即1000次,为一个定时周期
        /*时钟预分频系数为71,则驱动计数器的时钟CK_CNT=CK_INT/(1440-1+1)=0.05MHZ*/
        TIM_TimeStructure.TIM_Prescaler=psc-1;;//1400  //即定时器的频率为5KHZ   
        /*设置时钟分割,TIM_CKD_DIV1=0,PWM波不延时*/
        TIM_TimeStructure.TIM_ClockDivision=TIM_CKD_DIV1;
        /*向上计数模式*/
        TIM_TimeStructure.TIM_CounterMode=TIM_CounterMode_Up;
        /*重复计数器*/
        TIM_TimeStructure.TIM_RepetitionCounter=0;
            /*初始化定时器*/
        TIM_TimeBaseInit(TIMx,&TIM_TimeStructure);
        /*使能ARR预装载寄存器(影子寄存器)*/
        TIM_ARRPreloadConfig(TIMx,ENABLE);
        //-----------------输出比较结构体初始化 开始-----------------------/
        TIM_OCInitTypeDef   TIM_OCInitStructure;
        /*PWM模式设置,设置为PWM模式1*/
        TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1;
        /*PWM输出使能相应的IO口输出信号*/
        TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;
        /*设置占空比大小,CCR1[15:0]: 捕获/比较通道1的值,若CC1通道配置为输出:CCR1包含了装入当前捕获/比较1寄存器的值(预装载值)。*/
        TIM_OCInitStructure.TIM_Pulse=duty;   //占空比大小
        /*输出通道电平极性设置*/
        TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_High;
        /*初始化输出比较参数*/
        TIM_OC2Init(TIMx,&TIM_OCInitStructure);
        //-----------------输出比较结构体初始化 结束-----------------------/    
        /*自动重装载*/
        TIM_OC2PreloadConfig(TIMx,TIM_OCPreload_Enable);
        /*使能计数器*/
        TIM_Cmd(TIMx,ENABLE);
        /*主输出使能,如果设置了相应的使能位(TIMx_CCER寄存器的CCxE、CCxNE位),则开启OC和OCN输出。*/
        TIM_CtrlPWMOutputs(TIMx,ENABLE);    
    }
    /*舵机PWM初始化
       每增加0.1ms 舵机对应增加9度
    0.5ms---------0
    1.0ms---------45   
    1.5ms---------90
    2.0ms---------135
    2.5ms-----------180
    2.1ms    turn_left=150
    0.8ms    turn_right=25
    1.3ms    turn_front=75
    20ms的时基脉冲,如果想让舵机转90度,就应该发生一个高电平为1.5ms,周期为20ms的方波,duty=1.5/20=7.5% ,而定时器自动重装载寄存器arr的值为 1000 ,所以duty=75,时占空比为75/1000=7.5%. 
    */
    void SERVO_Init(void)
    {
        SERVO_Gpio_Init();
        SERVO_TIM_Config(TIM3,1000,1440,turn_front);
    /** 我们把定时器设置自动重装载寄存器 arr 的值为 1000,设置时钟预分频器为 1440,则
    驱动计数器的时钟:CK_CNT = CK_INT / (1440-1+1)=0.05M,则计数器计数一次的时间等于:
    1/CK_CNT=20us,当计数器计数到 ARR 的值 1000 时,产生一次中断,则中断一次的时间
    为:1/CK_CNT*ARR=20ms。
    PWM 信号的频率 f = TIM_CLK/{(ARR+1)*(PSC+1)}  TIM_CLK=72MHZ
                   = 72 000 000/(1000*1440)=5KHZ    
    */    
    }
    /*舵机角度控制*/
    void SERVO_Angle_Control(uint16_t Compare2)
    {
        TIM_SetCompare2(TIM3,Compare2);
    }
    

    编码器模块

    调节小车前进的速度和避障快慢我们采用EC11旋转式编码器,可以用于光度、湿度、音量调节等参数的调节。EC11编码器的形状类似于电位器,中心有一个旋钮可以调节PWM信号,光电码盘利用光电转换原理输出三组方波脉冲。EC11编码器的实物图如下:


    用来显示小车转速,以及左右编码器数值和电池电压等参数所用的是OLED显示模块,分辨率较高,而且功耗低,正常显示时仅0.06W,供电电压范围在3.3V-5V,有IIC和SPI两种通信协议可供选择。显示模块的亮度和对比度可以通过程序设置。由于它使用寿命长以及其他的优点,OLED更加适合小系统,本系统由于单片机引脚有限,不适合利用简单的LCD1602或者12864来显示,在多方对比之下OLED效果更好。OLED显示部分相对比较简单,大家参考中景园的例程就可以实现。


    展开全文
  • stm32 超声波避障小车2

    2017-12-14 18:59:09
    stm32 超声波避障小车2stm32 超声波避障小车2stm32 超声波避障小车2stm32 超声波避障小车2
  • 基于stm32超声波避障加遍历的智能小车程序通过超声波扫描构建局部地图再通过局部地图规划小车路径达到遍历与避障
  • stm32-超声波避障程序案例,循迹+壁障+遥控功能,蓝牙遥控
  • 这是一款基于STM32F407开发板的蓝牙控制小车的keil程序。其中包含有步进电机的PWM控制,HC-05蓝牙模块控制小车前进后退转向,HC-SR04超声波避障、DHT-11温湿度传感器上传温湿度、光敏电阻等。
  • 基于STM32超声波避障小车

    千次阅读 多人点赞 2020-10-05 17:15:38
    超声波避障小车原理就是通过给超声波模块超过10us的高电平信号,自动发送8个40KHZ的方,来检测是不是有信号的返回,如果有信号的返回,那么就判断为前方有障碍物,并且通过舵机云台,来实现180度的旋转,检测左右...

    超声波避障小车原理就是通过给超声波模块超过10us的高电平信号,自动发送8个40KHZ的方波,来检测是不是有信号的返回,如果有信号的返回,那么就判断为前方有障碍物,并且通过舵机云台,来实现180度的旋转,检测左右两边是否的有障碍物,从而进行避障的功能。

    说完原理,接下来就是根据各部件的原理来进行编程,我的超声波避障小车主要涉及了超声波HC-SR04模块,L298N电机驱动模块,舵机sg90模块。
    首先是最基本的电机驱动模块的相关代码:

    #include "bsp_motor.h"
    #include "delay.h"
    #include <math.h>
    
    void TIM4_PWM_Motor(unsigned int arr,unsigned int psc)
    {	
    	GPIO_InitTypeDef					GPIO_InitStuct;
    	TIM_TimeBaseInitTypeDef		TIM_TimeBaseInitStuct;
    	TIM_OCInitTypeDef					TIM_OCInitStuct;
    	
    	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB, ENABLE);
    	
    	
    	GPIO_InitStuct.GPIO_Pin=GPIO_Pin_7;						//左电机控制
    	GPIO_InitStuct.GPIO_Mode=GPIO_Mode_Out_PP;
    	GPIO_InitStuct.GPIO_Speed=GPIO_Speed_50MHz;
    	GPIO_Init(GPIOB, &GPIO_InitStuct);
    	
    	GPIO_InitStuct.GPIO_Pin=GPIO_Pin_8;						//左电机PWM
    	GPIO_InitStuct.GPIO_Mode=GPIO_Mode_AF_PP;
    	GPIO_InitStuct.GPIO_Speed=GPIO_Speed_50MHz;
    	GPIO_Init(GPIOB, &GPIO_InitStuct);
    	
    	GPIO_InitStuct.GPIO_Pin=GPIO_Pin_9;						//右电机PWM
    	GPIO_InitStuct.GPIO_Mode=GPIO_Mode_AF_PP;
    	GPIO_InitStuct.GPIO_Speed=GPIO_Speed_50MHz;
    	GPIO_Init(GPIOB, &GPIO_InitStuct);
    		
    	GPIO_InitStuct.GPIO_Pin=GPIO_Pin_4;						//右电机方向控制
    	GPIO_InitStuct.GPIO_Mode=GPIO_Mode_Out_PP;
    	GPIO_InitStuct.GPIO_Speed=GPIO_Speed_50MHz;
    	GPIO_Init(GPIOA, &GPIO_InitStuct);
    	
    	TIM_TimeBaseInitStuct.TIM_ClockDivision=0;
    	TIM_TimeBaseInitStuct.TIM_CounterMode=TIM_CounterMode_Up;
    	TIM_TimeBaseInitStuct.TIM_Period=arr;
    	TIM_TimeBaseInitStuct.TIM_Prescaler=psc;
    	TIM_TimeBaseInit(TIM4, &TIM_TimeBaseInitStuct);
    	
    	TIM_OCInitStuct.TIM_OCMode = TIM_OCMode_PWM2; 
    	TIM_OCInitStuct.TIM_OutputState = TIM_OutputState_Enable; 
    	TIM_OCInitStuct.TIM_Pulse = 0; 
    	TIM_OCInitStuct.TIM_OCPolarity = TIM_OCPolarity_High; 
    	TIM_OC3Init(TIM4, &TIM_OCInitStuct);  
    	TIM_OC4Init(TIM4, &TIM_OCInitStuct);  
    	
    	TIM_CtrlPWMOutputs(TIM4,ENABLE);	
      
    	TIM_OC3PreloadConfig(TIM4, TIM_OCPreload_Enable);  
    	TIM_OC4PreloadConfig(TIM4, TIM_OCPreload_Enable); 
    	
     	TIM_Cmd(TIM4, ENABLE);
    }
    
    void car_run(unsigned char ucChannel,signed char cSpeed)
    {
    		short PWM;	
       	PWM = 7201 - fabs(cSpeed)*72;
    	switch(ucChannel)
    	{
    		case 0://右轮
    			TIM_SetCompare3(TIM4,PWM);
    			if (cSpeed>0) 
    				GPIO_ResetBits(GPIOA,GPIO_Pin_4);
    			else if(cSpeed<0) 
    				GPIO_SetBits(GPIOA,GPIO_Pin_4);		
    			break;
    		case 1://左轮
    		  TIM_SetCompare4(TIM4,PWM); 
    			if (cSpeed>0) 
    				GPIO_SetBits(GPIOB,GPIO_Pin_7);
    			else if (cSpeed<0)
    				 GPIO_ResetBits(GPIOB,GPIO_Pin_7);
    			break;			
    	}
    }
    
    
    void Run_Go(signed char speed,int time)  //前进函数
    {
    		  signed char f_speed = - speed;
    	    car_run(1,f_speed);
    			car_run(0,speed);
    			delay_ms(time);                
    }
    
    void Run_stop(int time) //刹车函数
    {
    	  car_run(1,0);
    	  car_run(0,0);
    	  GPIO_ResetBits(GPIOA,GPIO_Pin_4);;
    	  GPIO_ResetBits(GPIOB,GPIO_Pin_7);;
    		delay_ms(time);           
    }
    
    void Run_left(signed char speed,int time) //左转
    {
    	  car_run(1,0);     
    	  car_run(0,speed);    
    		delay_ms(time);                 
    }
    void Run_Spin_left(signed char speed,int time) //左旋转
    {
    	  signed char u_speed = 100 - speed; 
     	  car_run(1,speed);  
    	  car_run(0,u_speed);          
    		delay_ms(time);                 
    
    }
    void Run_right(signed char speed,int time)  //右转
    {
    	  signed char f_speed = - speed;
    	  car_run(1,f_speed);   
    	  car_run(0,0);                
    		delay_ms(time);                  
    }
    void Run_Spin_right(signed char speed,int time) //右旋转
    {
    	  signed char u_speed = 100 - speed;
    	  signed char f_speed = - speed;
    	  car_run(1,-u_speed);   
    	  car_run(0,f_speed);           
    		delay_ms(time);                   
    }
    void Run_back(signed char speed,int time) //后退
    {
    	  signed char u_speed = 100- speed;
    	  signed char f_speed = - u_speed;
    	  car_run(1,u_speed);
    	  car_run(0,f_speed);
    		delay_ms(time);                 
    }
    

    超声波检测距离的相关代码:

    #include "bsp_hc.h"
    #include "delay.h"
    #define	TRIG_PORT      GPIOC		
    #define	ECHO_PORT      GPIOC		
    #define	TRIG_PIN       GPIO_Pin_0   
    #define	ECHO_PIN       GPIO_Pin_1
    
    float UltrasonicWave_Distance;     
    void UltrasonicWave_Configuration(void)
    {
      GPIO_InitTypeDef GPIO_InitStructure;	
    	EXTI_InitTypeDef EXTI_InitStructure;
     	NVIC_InitTypeDef NVIC_InitStructure;
    	
      RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC|RCC_APB2Periph_AFIO, ENABLE);
        
      GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;					  
      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 
      GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;	         
      GPIO_Init(GPIOC, &GPIO_InitStructure);	      
    
      GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;				   
      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;		 
      GPIO_Init(GPIOC,&GPIO_InitStructure);				
    	
    
     	GPIO_EXTILineConfig(GPIO_PortSourceGPIOC,GPIO_PinSource1);
    
     	EXTI_InitStructure.EXTI_Line = EXTI_Line1;
      EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;	
      EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
      EXTI_InitStructure.EXTI_LineCmd = ENABLE;
      EXTI_Init(&EXTI_InitStructure);		                        
    		
    			
    	NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;			     
      NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;	 
      NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;			  
      NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;						
      NVIC_Init(&NVIC_InitStructure);  	                       
    }
    
    void EXTI1_IRQHandler(void)
    {
    	delay_us(10);		                     
      if(EXTI_GetITStatus(EXTI_Line1) != RESET)
    	{
    			TIM_SetCounter(TIM2,0);
    			TIM_Cmd(TIM2, ENABLE);                                      
    		
    			while(GPIO_ReadInputDataBit(GPIOC,ECHO_PIN));	         
    
    			TIM_Cmd(TIM2, DISABLE);			                                
    			UltrasonicWave_Distance=TIM_GetCounter(TIM2)*5*34/200.0;			
    			EXTI_ClearITPendingBit(EXTI_Line1); 
    	}
    }
    void Timerx_Init(uint16_t arr,uint16_t psc)
    {
      TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    	NVIC_InitTypeDef NVIC_InitStructure;
    
    	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
    
    	TIM_TimeBaseStructure.TIM_Period = 5000;
    	TIM_TimeBaseStructure.TIM_Prescaler =(7200-1); 
    	TIM_TimeBaseStructure.TIM_ClockDivision = 0; 
    	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  
    	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); 
     
    	TIM_ITConfig(TIM2,TIM_IT_Update|TIM_IT_Trigger,ENABLE);
    	NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;  
    	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;  
    	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;  
    	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; 
    	NVIC_Init(&NVIC_InitStructure);  
    							 
    }
    
    void TIM2_IRQHandler(void)   
    {
    	if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) 
    		{
    		TIM_ClearITPendingBit(TIM2, TIM_IT_Update  ); 
    		}
    }
    
    int UltrasonicWave_StartMeasure(void)
    {
      int temp;
    	GPIO_SetBits(TRIG_PORT,TRIG_PIN); 		  
      delay_us(20);		                    
      GPIO_ResetBits(TRIG_PORT,TRIG_PIN);
    	temp = UltrasonicWave_Distance*10;
    	return temp;
    }
    

    舵机云台的相关代码:

    #include "bsp_duoji.h"
    
    void TIM5_PWM_Init(u16 arr,u16 psc)
    {
    	GPIO_InitTypeDef GPIO_InitStructure;
    	TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
      TIM_OCInitTypeDef TIM_OCInitStructure;
    	
      RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    
    	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; 
    	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; 
    	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    	GPIO_Init(GPIOA, &GPIO_InitStructure);
    	
    
    	 TIM_TimeBaseStructure.TIM_Period = arr;
    	 TIM_TimeBaseStructure.TIM_Prescaler =psc;
    	 TIM_TimeBaseStructure.TIM_ClockDivision = 0;
    	 TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    	 TIM_TimeBaseInit(TIM5, &TIM_TimeBaseStructure); 
    	
    	 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
    	 TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    	 TIM_OCInitStructure.TIM_Pulse = 0;
    	 TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
    	 TIM_OC1Init(TIM5, &TIM_OCInitStructure);
    	 
    	 TIM_CtrlPWMOutputs(TIM5,ENABLE);	
    	 TIM_OC1PreloadConfig(TIM5, TIM_OCPreload_Enable); 
       TIM_OC2Init(TIM5, &TIM_OCInitStructure);
    	 
    	 TIM_ARRPreloadConfig(TIM5, ENABLE); 
       TIM_Cmd(TIM5, ENABLE); 
    }
    
    

    最后是主函数:

    #include "stm32f10x.h"
    #include "bsp_motor.h"
    #include "bsp_duoji.h"
    #include "bsp_hc.h"
    #include "delay.h"
    
    void duojizhuantou(float angle)
    {
    	angle=(u16)(50.0*angle/9.0+249.0);
    	TIM_SetCompare1(TIM5,angle);
    }
    
    int front_detection()
    {
    	duojizhuantou(90);
    	delay_ms(100);
    	return UltrasonicWave_StartMeasure();
    }
    int left_detection()
    {
    	duojizhuantou(175);
    	delay_ms(300);
    	return UltrasonicWave_StartMeasure();
    }
    int right_detection()
    {
    	duojizhuantou(5);
    	delay_ms(300);
    	return UltrasonicWave_StartMeasure();
    }
    
    int main(void)
    {
      Timerx_Init(4999,7199);   
    	HC_GPIO();              
    	TIM4_PWM_Motor(7199,0);
    	TIM5_PWM_Init(9999,143); 
    	
    	while(1)
    	{  		
    		if(front_detection()<60 && front_detection()>0) 
    		{
    			Run_stop(500);		
    			Run_back(60,500);	
    			Run_stop(1000);			
    			left_detection();
    			delay_ms(500);
    			right_detection();
    			delay_ms(500);
    			if((left_detection() < 60 ) &&( right_detection() < 60 ))
    			{
    				Run_Spin_left(60,1000);
    			}				
          else if(left_detection() > right_detection())
    			{
    				Run_left(60,1000);
    				Run_stop(500);
    			}	
          else
    			{
    				Run_right(60,1000);
    				Run_stop(500);					
    			}							
    		}	
    		else
    		{
    			Run_Go(60,10);
    		}
    	}
     }
    
    展开全文
  • stm32超声波避障小车

    2020-12-28 21:55:38
    舵机+超声波避障小车
  • 不管是对于初学者还是对于一个玩过单片机的电子爱好者来说,或多或少都接触到过小车项目,今天给大家介绍的的一个项目基于STM32超声波避障小车。这也是我曾经的一个课设,在此开源分享给大家,全文5000多字,干货满...

    35e5c6af5e08a419d3552981c31e598f.png

    不管是对于初学者还是对于一个玩过单片机的电子爱好者来说,或多或少都接触到过小车项目,今天给大家介绍的的一个项目基于STM32超声波避障小车。这也是我曾经的一个课设,在此开源分享给大家,全文5000多字,干货满满,加油读完,保证你收货多多!

    735251b51664c986e3ff9a8b88cf465a.png
    基于STM32超声波避障小车https://www.zhihu.com/video/1208122319123783680

    处理器电路设计

    单片机是系统的CPU,是系统稳定、正常运行的重要前提,以下为单片机选型的两种方案:
    (1)传统的8位单片机,是通过超大规模集成电路对其进行集成为一个独立芯片的控制器。内部组件包括CPU、随机存储器、只读存储器、I/O接口、中断系统、计时器、串口通讯、数模转换等。STC89C52单片机是最常见的51单片机,但是资源较少,精确度低,处理速度相比STM32单片机差很多。

    (2)使用目前市面上最常见的STM32单片机,STM32系列单片机可分为ARMCortex-M3内核体系结构的不同应用领域。它可分为STM32F1系列和STM32F4系列,STM32F1系列单片机时钟频率最高可达72米,在同一产品中性能最好。单片机的基本处理速度为36米,16位单片机的性能也很好。微晶片的内建快闪记忆体相当大,范围从32kb到512kb,可视需要选择。单个设备的功耗非常低,仅360mA,32位单片机产品的功耗最低,每兆赫只有0.5安培。特别值得一提的是,内接单晶片汇流排是一种Harvard架构,可执行速度高达1.25 DMIPS/MHz的指令。此芯片越来越多地被用作主要控制器。

    通过对单片机的资源和处理时间的速度我们采用选择STM32103C8T6为本系统主控芯片,程序下载是只需要一个JLINK就可以轻松完成。控制器模块电路如下所示:

    0b2457d4cbb15dafbf399be50b2e9289.png

    电源模块设计

    本设计采用锂电池供电, 模块的供电电压一般都为5V,同时超声波模块需要较大的电流才能正常工作,所以在降压的基础上也要保证足够大的输出电流。本设计采用可调输出版本,模块的输入电压范围广,输出电压在1.25V-35V内可调,电压转换效率高,输出纹波小。降压电路如下所示:

    828c3d48beb51d4d61073a3d04aaede3.png

    电机驱动模块设计

    要完成转向是能够利用单片机实现的,然而单片机I0的带负载能力弱,因此我们选择了大功率放大器件TB6612FNG。TB6612FNG是采用MOSFET-H桥结构的双通道大电流电路输出,可以控制2个电机的驱动。相比普通的电机驱动,外围电路非常简单,只需要一个芯片和一个钽电容进行PWM输出滤波,系统尺寸小。PWM信号输入频率范围广,轻松满足本设计的需求。

    f2504e4efdc8350c5a0033893b01bf75.png

    电机驱动引脚表

     1控制芯片:TB6612
     2控制芯片数量:2
     3    1号TB6612引脚分配:
     4     VM         PWMA--------->TIM1_CH1(PA8)
     5     VCC        AIN2--------->GPIOB_12
     6     GND        AIN1--------->GPIOB_13
     7     AO1        STBY--------->GPIOB_14
     8     AO2        BIN1--------->GPIOB_15
     9     BO2        BIN2--------->GPIOA_12
    10     BO1        PWMB--------->TIM1_CH2(PA9)
    11     GND        GND
    12    2号TB6612引脚分配:
    13     VM         PWMA--------->TIM1_CH3(PA10)
    14     VCC        AIN2--------->GPIOB_5
    15     GND        AIN1--------->GPIOB_6
    16     AO1        STBY--------->GPIOB_7
    17     AO2        BIN1--------->GPIOB_8
    18     BO2        BIN2--------->GPIOA_9
    19     BO1        PWMB--------->TIM1_CH4(PA11)
    20     GND        GND
    21真值表
    22     AIN1   0     1     0     1
    23     AIN2   0     0     1     1
    24     BIN1   0     1     0     1
    25     BIN2   0     0     1     1
    26           停止  正转  反转  刹车

    电机所用到的定时器配置

     1//初始化TIMX,设置TIMx的ARR,PSC
     2//arr:自动重装载初值,psc为预分频值,两者配合控制定时器时钟的周期
     3//定时器选择TIM1
     4static void TB6612_ADVANCE_TIM1_Mode_Config(TIM_TypeDef* TIMx,uint16_t arr,uint16_t psc,uint16_t duty)
     5{
     6    //-----------------时基结构体初始化-------------------------/
     7    TIM_TimeBaseInitTypeDef TIM_TimeStructure;
     8    /*开启定时器1时钟,即内部时钟CK_INT=72M*/
     9    RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE);
    10    TIM_DeInit(TIMx);
    11    /*内部时钟作为计数器时钟,72MHZ*/
    12    TIM_InternalClockConfig(TIMx);
    13    /*自动重装载寄存器的值,累计TIM_Period+1个频率后产生一个更新或者中断*/
    14    TIM_TimeStructure.TIM_Period=arr;
    15    /*时钟预分频系数为71,则驱动计数器的时钟CK_CNT=CK_INT/(71+1)=1MHZ*/
    16    TIM_TimeStructure.TIM_Prescaler=psc-1;
    17    /*设置时钟分割,TIM_CKD_DIV1=0,PWM波不延时*/
    18    TIM_TimeStructure.TIM_ClockDivision=TIM_CKD_DIV1;
    19    /*向上计数模式*/
    20    TIM_TimeStructure.TIM_CounterMode=TIM_CounterMode_Up;
    21    /*重复计数器*/
    22    TIM_TimeStructure.TIM_RepetitionCounter=0;
    23    /*初始化定时器*/
    24    TIM_TimeBaseInit(TIMx,&TIM_TimeStructure);
    25    /*使能ARR预装载寄存器(影子寄存器)*/
    26    TIM_ARRPreloadConfig(TIMx,ENABLE);
    27    //-----------------输出比较结构体初始化-----------------------/
    28    TIM_OCInitTypeDef   TIM_OCInitStructure;
    29    /*PWM模式设置,设置为PWM模式1*/
    30    TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1;
    31    /*PWM输出使能相应的IO口输出信号*/
    32    TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;
    33    /*设置占空比大小,CCR1[15:0]: 捕获/比较通道1的值,若CC1通道配置为输出:CCR1包含了装入当前捕获/比较1寄存器的值(预装载值)。*/
    34    TIM_OCInitStructure.TIM_Pulse=duty;
    35    /*输出通道电平极性设置*/
    36    TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_High;
    37    /*初始化输出比较参数*/
    38    TIM_OC1Init(TIMx,&TIM_OCInitStructure);//初始化TIM1 通道1
    39    TIM_OC2Init(TIMx,&TIM_OCInitStructure);//初始化TIM1 通道2
    40    TIM_OC3Init(TIMx,&TIM_OCInitStructure);//初始化TIM1 通道3
    41    TIM_OC4Init(TIMx,&TIM_OCInitStructure);//初始化TIM1 通道4
    42    /*自动重装载*/
    43    TIM_OC1PreloadConfig(TIMx,TIM_OCPreload_Enable);
    44    TIM_OC2PreloadConfig(TIMx,TIM_OCPreload_Enable);
    45    TIM_OC3PreloadConfig(TIMx,TIM_OCPreload_Enable);
    46    TIM_OC4PreloadConfig(TIMx,TIM_OCPreload_Enable);
    47    /*使能计数器*/
    48    TIM_Cmd(TIMx,ENABLE);
    49    /*主输出使能,如果设置了相应的使能位(TIMx_CCER寄存器的CCxE、CCxNE位),则开启OC和OCN输出。*/
    50    TIM_CtrlPWMOutputs(TIMx,ENABLE);    
    51}
    52//高级定时器输出通道初始化函数
    53static void TB6612_ADVANCE_TIM_Gpio_Config()
    54{
    55    GPIO_InitTypeDef  GPIO_InitStruct;
    56    /*----------通道1配置--------------*/
    57    /*定时器1输出比较通道*/
    58    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
    59    /*配置为复用推挽输出*/
    60    GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;
    61    GPIO_InitStruct.GPIO_Pin=GPIO_Pin_8;
    62    GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
    63    GPIO_Init(GPIOA,&GPIO_InitStruct);
    64    /*-----------通道二配置-------------*/
    65    /*定时器1输出比较通道*/
    66    /*配置为复用推挽输出*/
    67    GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;
    68    GPIO_InitStruct.GPIO_Pin=GPIO_Pin_11;
    69    GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
    70    GPIO_Init(GPIOA,&GPIO_InitStruct);
    71     /*-----------通道三配置-------------*/
    72    /*定时器1输出比较通道*/
    73    /*配置为复用推挽输出*/
    74    GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;
    75    GPIO_InitStruct.GPIO_Pin=GPIO_Pin_9;
    76    GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
    77    GPIO_Init(GPIOA,&GPIO_InitStruct);
    78     /*-----------通道四配置-------------*/
    79    /*定时器1输出比较通道*/
    80    /*配置为复用推挽输出*/
    81    GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;
    82    GPIO_InitStruct.GPIO_Pin=GPIO_Pin_10;
    83    GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
    84    GPIO_Init(GPIOA,&GPIO_InitStruct);
    85}

    超声波模块

    采用HC-SR04超声波模块,该芯片具有较高的集成度以及良好的稳定性,测度距离十分精确,十分稳定。供电电压为DC5V供电电流小于10mA,探测距离为0.010m-3.5m一共有四个引脚VCC(DC5V)、Triger(发射端)、Echo(接收端)、GND(地)。HC-SR04实物图如下:

    879a177170d1f8b6ab8d5a5b48be4769.png

    该模块是利用单片机的IO触发电平测距,单片机内部利用普通定时器产生一个高电平信号之后,超声波就可以自主发送频率为40khz的方波,然后等待信号的返回;若有信号返回,单片机IO口就立刻输出一高电平,利用高电平产生的时间可以计算小车与障碍物的距离。最终距离就是高电平持续时间乘以声音在空气中传播的速度再除以2,可以反复测量距离。

    在程序开始首先初始化超声波,利用定时器并设置时基的自动重装载初值1000,psc为预分频值72,这样的话我们产生一次中断的时间是1ms,并设置抢占优先级0,子优先级3。HC_SR04_Echo引脚接收到高电平,打开定时器,且每1ms进入一次中断。在测量时首先让Trig发送一个大于10us的高电平,然后拉高HC_SR04_Trig,当Echo为0时打开定时器计时,当Echo为1时关闭定时器,通过公式计算距离。

    模块工作原理:
    (1)单片机触发引脚,输出高电平信号;
    (2)模块发送端自动发送特定频率的方波;
    (3)如果有信号返回,通过IO输出一高电平,高电平持续的时间就是超声波的发射时长;
    (4)测试距离=(高电平时间*声速(340M/S))/2。

    注意:在硬件操作上需要首先让模块地端先连接,否则会影响模块工作。测距时,被测物体的摆放不能太过于杂乱,否则会影响测试结果。

    f7a0d5b5870a925ec4ddae105fc914a1.png

    超声波重要代码(可参考)

     1/* 获取接收到的高电平的时间(us*/
     2uint32_t Get_HC_SR04_Time(void)
     3{
     4    uint32_t t=0;
     5    t=Acoustic_Distance_Count*1000;//us
     6    t+=TIM_GetCounter(TIM2);//获取us
     7    TIM2->CNT =0;
     8    Acoustic_Distance_Count=0;
     9    Systic_Delay_us(100);
    10    return t;
    11}
    12/*获取距离*/
    13void Get_HC_SR04_Distance(void)
    14{
    15    static uint16_t count=0;
    16    switch(count)
    17    {
    18        case 1:
    19        {
    20            GPIO_SetBits(Acoustic_Port,HC_SR04_Trig);//Trig发送一个大于10us的高电平
    21        }break;
    22
    23        case 15:
    24        {
    25            count=0;
    26            GPIO_ResetBits(Acoustic_Port,HC_SR04_Trig);
    27            while(GPIO_ReadInputDataBit(Acoustic_Port,HC_SR04_Echo)==0);//当Echo为0时打开定时器 计时
    28            Open_Tim2();
    29            while(GPIO_ReadInputDataBit(Acoustic_Port,HC_SR04_Echo)==1);//当Echo为0时打开定时器 计时
    30            Close_Tim2();
    31            HC_SR04_Distance=(float)(Get_HC_SR04_Time()/5.78);
    32
    33        }break;
    34        default:break;
    35    }
    36    count++;
    37}

    舵机模块

    本系统使用的是SG90型号的舵机,舵机是一种常见的角度驱动器,本系统需要判断不同位置的障碍物可以且对转向的力度小。舵机可以理解为方向盘称,方向盘是一个常见的名字。它实际上是一个伺服马达。舵机实物图如下:

    bd3b32b58dab70359413d8fed2b37696.png

    舵机模块接口简单,舵机模块只有三个引脚。分别引引出了三根线左右两边是电源正负接口线,中间一根是PWM信号线直接连接单片机的控制引脚。通过控制单片机的引脚输出的脉冲宽度进而控制舵机旋转的角度。舵机每增加0.1ms 舵机对应增加9度。

    0.5ms---------0

    1.0ms---------45

    1.5ms---------90

    2.0ms---------135

    2.5ms-----------180

    20ms的时基脉冲,如果想让舵机转90度,就应该发生一个高电平持续时间为1.5ms,周期为20ms的方波,duty=1.5/20=7.5%。在这里设置定时器自动重装载寄存器arr的值为1000,所以当占空比为百分之75是,在程序中就要设置占空比为75/1000=7.5%, 这就是具体的算法。

    舵机重要代码(可参考)

     1/**PWM引脚初始化*/
     2static void SERVO_Gpio_Init(void)
     3{
     4    GPIO_InitTypeDef  GPIO_InitStruct;
     5    /*----------通道2配置--------------*/
     6    /*定时器3输出比较通道*/
     7    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
     8    /*配置为复用推挽输出*/
     9    GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;
    10    GPIO_InitStruct.GPIO_Pin=GPIO_Pin_7;
    11    GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
    12    GPIO_Init(GPIOA,&GPIO_InitStruct); 
    13}
    14//定时器3初始化,设置TIMx的ARR,PSC
    15//arr:自动重装载初值,psc为预分频值,两者配合控制定时器时钟的周期
    16static void SERVO_TIM_Config(TIM_TypeDef* TIMx,uint16_t arr,uint16_t psc,uint16_t duty)
    17{
    18    //-----------------时基结构体初始化-------------------------/
    19    TIM_TimeBaseInitTypeDef TIM_TimeStructure;
    20    /*开启定时器3时钟,即内部时钟CK_INT=72M*/
    21    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
    22    TIM_DeInit(TIMx);
    23    /*内部时钟作为计数器时钟,72MHZ*/
    24    TIM_InternalClockConfig(TIMx);
    25    /*自动重装载寄存器的值,累计TIM_Period+1个频率后产生一个更新或者中断*/
    26    TIM_TimeStructure.TIM_Period=arr;//1000 当定时器从0计数到999,即1000次,为一个定时周期
    27    /*时钟预分频系数为71,则驱动计数器的时钟CK_CNT=CK_INT/(1440-1+1)=0.05MHZ*/
    28    TIM_TimeStructure.TIM_Prescaler=psc-1;;//1400  //即定时器的频率为5KHZ   
    29    /*设置时钟分割,TIM_CKD_DIV1=0,PWM波不延时*/
    30    TIM_TimeStructure.TIM_ClockDivision=TIM_CKD_DIV1;
    31    /*向上计数模式*/
    32    TIM_TimeStructure.TIM_CounterMode=TIM_CounterMode_Up;
    33    /*重复计数器*/
    34    TIM_TimeStructure.TIM_RepetitionCounter=0;
    35        /*初始化定时器*/
    36    TIM_TimeBaseInit(TIMx,&TIM_TimeStructure);
    37    /*使能ARR预装载寄存器(影子寄存器)*/
    38    TIM_ARRPreloadConfig(TIMx,ENABLE);
    39    //-----------------输出比较结构体初始化 开始-----------------------/
    40    TIM_OCInitTypeDef   TIM_OCInitStructure;
    41    /*PWM模式设置,设置为PWM模式1*/
    42    TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1;
    43    /*PWM输出使能相应的IO口输出信号*/
    44    TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;
    45    /*设置占空比大小,CCR1[15:0]: 捕获/比较通道1的值,若CC1通道配置为输出:CCR1包含了装入当前捕获/比较1寄存器的值(预装载值)。*/
    46    TIM_OCInitStructure.TIM_Pulse=duty;   //占空比大小
    47    /*输出通道电平极性设置*/
    48    TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_High;
    49    /*初始化输出比较参数*/
    50    TIM_OC2Init(TIMx,&TIM_OCInitStructure);
    51    //-----------------输出比较结构体初始化 结束-----------------------/    
    52    /*自动重装载*/
    53    TIM_OC2PreloadConfig(TIMx,TIM_OCPreload_Enable);
    54    /*使能计数器*/
    55    TIM_Cmd(TIMx,ENABLE);
    56    /*主输出使能,如果设置了相应的使能位(TIMx_CCER寄存器的CCxE、CCxNE位),则开启OC和OCN输出。*/
    57    TIM_CtrlPWMOutputs(TIMx,ENABLE);    
    58}
    59/*舵机PWM初始化
    60   每增加0.1ms 舵机对应增加9度
    610.5ms---------0
    621.0ms---------45   
    631.5ms---------90
    642.0ms---------135
    652.5ms-----------180
    662.1ms    turn_left=150
    670.8ms    turn_right=25
    681.3ms    turn_front=75
    6920ms的时基脉冲,如果想让舵机转90度,就应该发生一个高电平为1.5ms,周期为20ms的方波,duty=1.5/20=7.5% ,而定时器自动重装载寄存器arr的值为 1000 ,所以duty=75,时占空比为75/1000=7.5%. 
    70*/
    71void SERVO_Init(void)
    72{
    73    SERVO_Gpio_Init();
    74    SERVO_TIM_Config(TIM3,1000,1440,turn_front);
    75/** 我们把定时器设置自动重装载寄存器 arr 的值为 1000,设置时钟预分频器为 1440,则
    76驱动计数器的时钟:CK_CNT = CK_INT / (1440-1+1)=0.05M,则计数器计数一次的时间等于:
    771/CK_CNT=20us,当计数器计数到 ARR 的值 1000 时,产生一次中断,则中断一次的时间
    78为:1/CK_CNT*ARR=20ms。
    79PWM 信号的频率 f = TIM_CLK/{(ARR+1)*(PSC+1)}  TIM_CLK=72MHZ
    80               = 72 000 000/(1000*1440)=5KHZ    
    81*/    
    82}
    83/*舵机角度控制*/
    84void SERVO_Angle_Control(uint16_t Compare2)
    85{
    86    TIM_SetCompare2(TIM3,Compare2);
    87}

    编码器模块

    调节小车前进的速度和避障快慢我们采用EC11旋转式编码器,可以用于光度、湿度、音量调节等参数的调节。EC11编码器的形状类似于电位器,中心有一个旋钮可以调节PWM信号,光电码盘利用光电转换原理输出三组方波脉冲。EC11编码器的实物图如下:

    4d70b11c6a04cfa143cae1e70a92acb4.png

    OLED显示模块

    用来显示小车转速,以及左右编码器数值和电池电压等参数所用的是OLED显示模块,分辨率较高,而且功耗低,正常显示时仅0.06W,供电电压范围在3.3V-5V,有IIC和SPI两种通信协议可供选择。显示模块的亮度和对比度可以通过程序设置。由于它使用寿命长以及其他的优点,OLED更加适合小系统,本系统由于单片机引脚有限,不适合利用简单的LCD1602或者12864来显示,在多方对比之下OLED效果更好。OLED显示部分相对比较简单,大家参考中景园的例程就可以实现。

    微信公众号:果果小师弟

    个人网站:http://www.liuyao-blog.cn

    展开全文
  • stm32 超声波避障小车1

    2017-12-14 09:20:17
    stm32 超声波避障小车1stm32 超声波避障小车1stm32 超声波避障小车1
  • 基于STM32小车超声波避障实验(有代码)

    千次阅读 多人点赞 2020-11-04 21:08:18
    超声波避障代码 #include "sys.h" #include "delay.h" #define EN1 PAout(2) //L293D控制管脚定义 #define IN1 PAout(3) //L293D控制管脚定义 #define IN2 PAout(4) //L293D控制管脚定义 #define EN2 PAout(7)

    超声波避障代码

    #include "sys.h"	
    #include "delay.h"	
    
    #define EN1 PAout(2)                          //L293D控制管脚定义
    #define IN1 PAout(3)                          //L293D控制管脚定义
    #define IN2 PAout(4)                          //L293D控制管脚定义
    #define EN2 PAout(7)                          //L293D控制管脚定义
    #define IN3 PAout(6)                          //L293D控制管脚定义
    #define IN4 PAout(5)                          //L293D控制管脚定义
    
    #define BEEP PBout(5)                         //蜂鸣器控制管脚定义
    
    #define KEY1 PAin(8)		                      //功能按键对应的管脚
    
    #define BZ_LEFT PAin(13)                      //左边避障信号           
    #define BZ_RIGHT PAin(14)                     //右边避障信号
    
    #define ControlPort PAout(0)                  //舵机控制接口
    
    #define Stop  0                               //舵机停标志
    #define Right 1	                              //舵机右转标志
    #define Left  2	                              //舵机左转标志
    
    #define OLED_RST_Clr() PCout(13)=0            //RST
    #define OLED_RST_Set() PCout(13)=1            //RST
    
    #define OLED_RS_Clr() PBout(4)=0              //DC
    #define OLED_RS_Set() PBout(4)=1              //DC
    
    #define OLED_SCLK_Clr() PCout(15)=0           //SCL
    #define OLED_SCLK_Set() PCout(15)=1           //SCL
    
    #define OLED_SDIN_Clr() PCout(14)=0           //SDA
    #define OLED_SDIN_Set() PCout(14)=1           //SDA
    
    #define OLED_CMD  0	                          //写命令
    #define OLED_DATA 1	                          //写数据
    
    u32 JuLi;                                     //超声波测距
    
    u8 pwmval_left  = 0;                          //左电机调速变量 
    u8 pwmval_right = 0;                          //右电机调速变量 
    
    u8 pwmval_left_init  = 6;                     //左电机速度值  
    u8 pwmval_right_init = 6;                     //右电机速度值 
    
    u8 right_pwm = 1;	                            //左电机调速开关   
    u8 left_pwm  = 1;			                        //右电机调速开关      
    
    u8 TimeOutCounter = 0;
    u8 LeftOrRight    = 0;
    u16 timer = 0;   
    
    u8 hw_flag;
    u32 S_temp;
    
    u8 OLED_GRAM[128][8];
    
    const unsigned char oled_asc2_1206[95][12]={
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*" ",0*/
    {0x00,0x00,0x00,0x00,0x3F,0x40,0x00,0x00,0x00,0x00,0x00,0x00},/*"!",1*/
    {0x00,0x00,0x30,0x00,0x40,0x00,0x30,0x00,0x40,0x00,0x00,0x00},/*""",2*/
    {0x09,0x00,0x0B,0xC0,0x3D,0x00,0x0B,0xC0,0x3D,0x00,0x09,0x00},/*"#",3*/
    {0x18,0xC0,0x24,0x40,0x7F,0xE0,0x22,0x40,0x31,0x80,0x00,0x00},/*"$",4*/
    {0x18,0x00,0x24,0xC0,0x1B,0x00,0x0D,0x80,0x32,0x40,0x01,0x80},/*"%",5*/
    {0x03,0x80,0x1C,0x40,0x27,0x40,0x1C,0x80,0x07,0x40,0x00,0x40},/*"&",6*/
    {0x10,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"'",7*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0x80,0x20,0x40,0x40,0x20},/*"(",8*/
    {0x00,0x00,0x40,0x20,0x20,0x40,0x1F,0x80,0x00,0x00,0x00,0x00},/*")",9*/
    {0x09,0x00,0x06,0x00,0x1F,0x80,0x06,0x00,0x09,0x00,0x00,0x00},/*"*",10*/
    {0x04,0x00,0x04,0x00,0x3F,0x80,0x04,0x00,0x04,0x00,0x00,0x00},/*"+",11*/
    {0x00,0x10,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*",",12*/
    {0x04,0x00,0x04,0x00,0x04,0x00,0x04,0x00,0x04,0x00,0x00,0x00},/*"-",13*/
    {0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*".",14*/
    {0x00,0x20,0x01,0xC0,0x06,0x00,0x38,0x00,0x40,0x00,0x00,0x00},/*"/",15*/
    {0x1F,0x80,0x20,0x40,0x20,0x40,0x20,0x40,0x1F,0x80,0x00,0x00},/*"0",16*/
    {0x00,0x00,0x10,0x40,0x3F,0xC0,0x00,0x40,0x00,0x00,0x00,0x00},/*"1",17*/
    {0x18,0xC0,0x21,0x40,0x22,0x40,0x24,0x40,0x18,0x40,0x00,0x00},/*"2",18*/
    {0x10,0x80,0x20,0x40,0x24,0x40,0x24,0x40,0x1B,0x80,0x00,0x00},/*"3",19*/
    {0x02,0x00,0x0D,0x00,0x11,0x00,0x3F,0xC0,0x01,0x40,0x00,0x00},/*"4",20*/
    {0x3C,0x80,0x24,0x40,0x24,0x40,0x24,0x40,0x23,0x80,0x00,0x00},/*"5",21*/
    {0x1F,0x80,0x24,0x40,0x24,0x40,0x34,0x40,0x03,0x80,0x00,0x00},/*"6",22*/
    {0x30,0x00,0x20,0x00,0x27,0xC0,0x38,0x00,0x20,0x00,0x00,0x00},/*"7",23*/
    {0x1B,0x80,0x24,0x40,0x24,0x40,0x24,0x40,0x1B,0x80,0x00,0x00},/*"8",24*/
    {0x1C,0x00,0x22,0xC0,0x22,0x40,0x22,0x40,0x1F,0x80,0x00,0x00},/*"9",25*/
    {0x00,0x00,0x00,0x00,0x08,0x40,0x00,0x00,0x00,0x00,0x00,0x00},/*":",26*/
    {0x00,0x00,0x00,0x00,0x04,0x60,0x00,0x00,0x00,0x00,0x00,0x00},/*";",27*/
    {0x00,0x00,0x04,0x00,0x0A,0x00,0x11,0x00,0x20,0x80,0x40,0x40},/*"<",28*/
    {0x09,0x00,0x09,0x00,0x09,0x00,0x09,0x00,0x09,0x00,0x00,0x00},/*"=",29*/
    {0x00,0x00,0x40,0x40,0x20,0x80,0x11,0x00,0x0A,0x00,0x04,0x00},/*">",30*/
    {0x18,0x00,0x20,0x00,0x23,0x40,0x24,0x00,0x18,0x00,0x00,0x00},/*"?",31*/
    {0x1F,0x80,0x20,0x40,0x27,0x40,0x29,0x40,0x1F,0x40,0x00,0x00},/*"@",32*/
    {0x00,0x40,0x07,0xC0,0x39,0x00,0x0F,0x00,0x01,0xC0,0x00,0x40},/*"A",33*/
    {0x20,0x40,0x3F,0xC0,0x24,0x40,0x24,0x40,0x1B,0x80,0x00,0x00},/*"B",34*/
    {0x1F,0x80,0x20,0x40,0x20,0x40,0x20,0x40,0x30,0x80,0x00,0x00},/*"C",35*/
    {0x20,0x40,0x3F,0xC0,0x20,0x40,0x20,0x40,0x1F,0x80,0x00,0x00},/*"D",36*/
    {0x20,0x40,0x3F,0xC0,0x24,0x40,0x2E,0x40,0x30,0xC0,0x00,0x00},/*"E",37*/
    {0x20,0x40,0x3F,0xC0,0x24,0x40,0x2E,0x00,0x30,0x00,0x00,0x00},/*"F",38*/
    {0x0F,0x00,0x10,0x80,0x20,0x40,0x22,0x40,0x33,0x80,0x02,0x00},/*"G",39*/
    {0x20,0x40,0x3F,0xC0,0x04,0x00,0x04,0x00,0x3F,0xC0,0x20,0x40},/*"H",40*/
    {0x20,0x40,0x20,0x40,0x3F,0xC0,0x20,0x40,0x20,0x40,0x00,0x00},/*"I",41*/
    {0x00,0x60,0x20,0x20,0x20,0x20,0x3F,0xC0,0x20,0x00,0x20,0x00},/*"J",42*/
    {0x20,0x40,0x3F,0xC0,0x24,0x40,0x0B,0x00,0x30,0xC0,0x20,0x40},/*"K",43*/
    {0x20,0x40,0x3F,0xC0,0x20,0x40,0x00,0x40,0x00,0x40,0x00,0xC0},/*"L",44*/
    {0x3F,0xC0,0x3C,0x00,0x03,0xC0,0x3C,0x00,0x3F,0xC0,0x00,0x00},/*"M",45*/
    {0x20,0x40,0x3F,0xC0,0x0C,0x40,0x23,0x00,0x3F,0xC0,0x20,0x00},/*"N",46*/
    {0x1F,0x80,0x20,0x40,0x20,0x40,0x20,0x40,0x1F,0x80,0x00,0x00},/*"O",47*/
    {0x20,0x40,0x3F,0xC0,0x24,0x40,0x24,0x00,0x18,0x00,0x00,0x00},/*"P",48*/
    {0x1F,0x80,0x21,0x40,0x21,0x40,0x20,0xE0,0x1F,0xA0,0x00,0x00},/*"Q",49*/
    {0x20,0x40,0x3F,0xC0,0x24,0x40,0x26,0x00,0x19,0xC0,0x00,0x40},/*"R",50*/
    {0x18,0xC0,0x24,0x40,0x24,0x40,0x22,0x40,0x31,0x80,0x00,0x00},/*"S",51*/
    {0x30,0x00,0x20,0x40,0x3F,0xC0,0x20,0x40,0x30,0x00,0x00,0x00},/*"T",52*/
    {0x20,0x00,0x3F,0x80,0x00,0x40,0x00,0x40,0x3F,0x80,0x20,0x00},/*"U",53*/
    {0x20,0x00,0x3E,0x00,0x01,0xC0,0x07,0x00,0x38,0x00,0x20,0x00},/*"V",54*/
    {0x38,0x00,0x07,0xC0,0x3C,0x00,0x07,0xC0,0x38,0x00,0x00,0x00},/*"W",55*/
    {0x20,0x40,0x39,0xC0,0x06,0x00,0x39,0xC0,0x20,0x40,0x00,0x00},/*"X",56*/
    {0x20,0x00,0x38,0x40,0x07,0xC0,0x38,0x40,0x20,0x00,0x00,0x00},/*"Y",57*/
    {0x30,0x40,0x21,0xC0,0x26,0x40,0x38,0x40,0x20,0xC0,0x00,0x00},/*"Z",58*/
    {0x00,0x00,0x00,0x00,0x7F,0xE0,0x40,0x20,0x40,0x20,0x00,0x00},/*"[",59*/
    {0x00,0x00,0x70,0x00,0x0C,0x00,0x03,0x80,0x00,0x40,0x00,0x00},/*"\",60*/
    {0x00,0x00,0x40,0x20,0x40,0x20,0x7F,0xE0,0x00,0x00,0x00,0x00},/*"]",61*/
    {0x00,0x00,0x20,0x00,0x40,0x00,0x20,0x00,0x00,0x00,0x00,0x00},/*"^",62*/
    {0x00,0x10,0x00,0x10,0x00,0x10,0x00,0x10,0x00,0x10,0x00,0x10},/*"_",63*/
    {0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"`",64*/
    {0x00,0x00,0x02,0x80,0x05,0x40,0x05,0x40,0x03,0xC0,0x00,0x40},/*"a",65*/
    {0x20,0x00,0x3F,0xC0,0x04,0x40,0x04,0x40,0x03,0x80,0x00,0x00},/*"b",66*/
    {0x00,0x00,0x03,0x80,0x04,0x40,0x04,0x40,0x06,0x40,0x00,0x00},/*"c",67*/
    {0x00,0x00,0x03,0x80,0x04,0x40,0x24,0x40,0x3F,0xC0,0x00,0x40},/*"d",68*/
    {0x00,0x00,0x03,0x80,0x05,0x40,0x05,0x40,0x03,0x40,0x00,0x00},/*"e",69*/
    {0x00,0x00,0x04,0x40,0x1F,0xC0,0x24,0x40,0x24,0x40,0x20,0x00},/*"f",70*/
    {0x00,0x00,0x02,0xE0,0x05,0x50,0x05,0x50,0x06,0x50,0x04,0x20},/*"g",71*/
    {0x20,0x40,0x3F,0xC0,0x04,0x40,0x04,0x00,0x03,0xC0,0x00,0x40},/*"h",72*/
    {0x00,0x00,0x04,0x40,0x27,0xC0,0x00,0x40,0x00,0x00,0x00,0x00},/*"i",73*/
    {0x00,0x10,0x00,0x10,0x04,0x10,0x27,0xE0,0x00,0x00,0x00,0x00},/*"j",74*/
    {0x20,0x40,0x3F,0xC0,0x01,0x40,0x07,0x00,0x04,0xC0,0x04,0x40},/*"k",75*/
    {0x20,0x40,0x20,0x40,0x3F,0xC0,0x00,0x40,0x00,0x40,0x00,0x00},/*"l",76*/
    {0x07,0xC0,0x04,0x00,0x07,0xC0,0x04,0x00,0x03,0xC0,0x00,0x00},/*"m",77*/
    {0x04,0x40,0x07,0xC0,0x04,0x40,0x04,0x00,0x03,0xC0,0x00,0x40},/*"n",78*/
    {0x00,0x00,0x03,0x80,0x04,0x40,0x04,0x40,0x03,0x80,0x00,0x00},/*"o",79*/
    {0x04,0x10,0x07,0xF0,0x04,0x50,0x04,0x40,0x03,0x80,0x00,0x00},/*"p",80*/
    {0x00,0x00,0x03,0x80,0x04,0x40,0x04,0x50,0x07,0xF0,0x00,0x10},/*"q",81*/
    {0x04,0x40,0x07,0xC0,0x02,0x40,0x04,0x00,0x04,0x00,0x00,0x00},/*"r",82*/
    {0x00,0x00,0x06,0x40,0x05,0x40,0x05,0x40,0x04,0xC0,0x00,0x00},/*"s",83*/
    {0x00,0x00,0x04,0x00,0x1F,0x80,0x04,0x40,0x00,0x40,0x00,0x00},/*"t",84*/
    {0x04,0x00,0x07,0x80,0x00,0x40,0x04,0x40,0x07,0xC0,0x00,0x40},/*"u",85*/
    {0x04,0x00,0x07,0x00,0x04,0xC0,0x01,0x80,0x06,0x00,0x04,0x00},/*"v",86*/
    {0x06,0x00,0x01,0xC0,0x07,0x00,0x01,0xC0,0x06,0x00,0x00,0x00},/*"w",87*/
    {0x04,0x40,0x06,0xC0,0x01,0x00,0x06,0xC0,0x04,0x40,0x00,0x00},/*"x",88*/
    {0x04,0x10,0x07,0x10,0x04,0xE0,0x01,0x80,0x06,0x00,0x04,0x00},/*"y",89*/
    {0x00,0x00,0x04,0x40,0x05,0xC0,0x06,0x40,0x04,0x40,0x00,0x00},/*"z",90*/
    {0x00,0x00,0x00,0x00,0x04,0x00,0x7B,0xE0,0x40,0x20,0x00,0x00},/*"{",91*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xF0,0x00,0x00,0x00,0x00},/*"|",92*/
    {0x00,0x00,0x40,0x20,0x7B,0xE0,0x04,0x00,0x00,0x00,0x00,0x00},/*"}",93*/
    {0x40,0x00,0x80,0x00,0x40,0x00,0x20,0x00,0x20,0x00,0x40,0x00},/*"~",94*/
    }; 
    const unsigned char oled_asc2_1608[95][16]={	  
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*" ",0*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0xCC,0x00,0x0C,0x00,0x00,0x00,0x00,0x00,0x00},/*"!",1*/
    {0x00,0x00,0x08,0x00,0x30,0x00,0x60,0x00,0x08,0x00,0x30,0x00,0x60,0x00,0x00,0x00},/*""",2*/
    {0x02,0x20,0x03,0xFC,0x1E,0x20,0x02,0x20,0x03,0xFC,0x1E,0x20,0x02,0x20,0x00,0x00},/*"#",3*/
    {0x00,0x00,0x0E,0x18,0x11,0x04,0x3F,0xFF,0x10,0x84,0x0C,0x78,0x00,0x00,0x00,0x00},/*"$",4*/
    {0x0F,0x00,0x10,0x84,0x0F,0x38,0x00,0xC0,0x07,0x78,0x18,0x84,0x00,0x78,0x00,0x00},/*"%",5*/
    {0x00,0x78,0x0F,0x84,0x10,0xC4,0x11,0x24,0x0E,0x98,0x00,0xE4,0x00,0x84,0x00,0x08},/*"&",6*/
    {0x08,0x00,0x68,0x00,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"'",7*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x07,0xE0,0x18,0x18,0x20,0x04,0x40,0x02,0x00,0x00},/*"(",8*/
    {0x00,0x00,0x40,0x02,0x20,0x04,0x18,0x18,0x07,0xE0,0x00,0x00,0x00,0x00,0x00,0x00},/*")",9*/
    {0x02,0x40,0x02,0x40,0x01,0x80,0x0F,0xF0,0x01,0x80,0x02,0x40,0x02,0x40,0x00,0x00},/*"*",10*/
    {0x00,0x80,0x00,0x80,0x00,0x80,0x0F,0xF8,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x00},/*"+",11*/
    {0x00,0x01,0x00,0x0D,0x00,0x0E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*",",12*/
    {0x00,0x00,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x80},/*"-",13*/
    {0x00,0x00,0x00,0x0C,0x00,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*".",14*/
    {0x00,0x00,0x00,0x06,0x00,0x18,0x00,0x60,0x01,0x80,0x06,0x00,0x18,0x00,0x20,0x00},/*"/",15*/
    {0x00,0x00,0x07,0xF0,0x08,0x08,0x10,0x04,0x10,0x04,0x08,0x08,0x07,0xF0,0x00,0x00},/*"0",16*/
    {0x00,0x00,0x08,0x04,0x08,0x04,0x1F,0xFC,0x00,0x04,0x00,0x04,0x00,0x00,0x00,0x00},/*"1",17*/
    {0x00,0x00,0x0E,0x0C,0x10,0x14,0x10,0x24,0x10,0x44,0x11,0x84,0x0E,0x0C,0x00,0x00},/*"2",18*/
    {0x00,0x00,0x0C,0x18,0x10,0x04,0x11,0x04,0x11,0x04,0x12,0x88,0x0C,0x70,0x00,0x00},/*"3",19*/
    {0x00,0x00,0x00,0xE0,0x03,0x20,0x04,0x24,0x08,0x24,0x1F,0xFC,0x00,0x24,0x00,0x00},/*"4",20*/
    {0x00,0x00,0x1F,0x98,0x10,0x84,0x11,0x04,0x11,0x04,0x10,0x88,0x10,0x70,0x00,0x00},/*"5",21*/
    {0x00,0x00,0x07,0xF0,0x08,0x88,0x11,0x04,0x11,0x04,0x18,0x88,0x00,0x70,0x00,0x00},/*"6",22*/
    {0x00,0x00,0x1C,0x00,0x10,0x00,0x10,0xFC,0x13,0x00,0x1C,0x00,0x10,0x00,0x00,0x00},/*"7",23*/
    {0x00,0x00,0x0E,0x38,0x11,0x44,0x10,0x84,0x10,0x84,0x11,0x44,0x0E,0x38,0x00,0x00},/*"8",24*/
    {0x00,0x00,0x07,0x00,0x08,0x8C,0x10,0x44,0x10,0x44,0x08,0x88,0x07,0xF0,0x00,0x00},/*"9",25*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x0C,0x03,0x0C,0x00,0x00,0x00,0x00,0x00,0x00},/*":",26*/
    {0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*";",27*/
    {0x00,0x00,0x00,0x80,0x01,0x40,0x02,0x20,0x04,0x10,0x08,0x08,0x10,0x04,0x00,0x00},/*"<",28*/
    {0x02,0x20,0x02,0x20,0x02,0x20,0x02,0x20,0x02,0x20,0x02,0x20,0x02,0x20,0x00,0x00},/*"=",29*/
    {0x00,0x00,0x10,0x04,0x08,0x08,0x04,0x10,0x02,0x20,0x01,0x40,0x00,0x80,0x00,0x00},/*">",30*/
    {0x00,0x00,0x0E,0x00,0x12,0x00,0x10,0x0C,0x10,0x6C,0x10,0x80,0x0F,0x00,0x00,0x00},/*"?",31*/
    {0x03,0xE0,0x0C,0x18,0x13,0xE4,0x14,0x24,0x17,0xC4,0x08,0x28,0x07,0xD0,0x00,0x00},/*"@",32*/
    {0x00,0x04,0x00,0x3C,0x03,0xC4,0x1C,0x40,0x07,0x40,0x00,0xE4,0x00,0x1C,0x00,0x04},/*"A",33*/
    {0x10,0x04,0x1F,0xFC,0x11,0x04,0x11,0x04,0x11,0x04,0x0E,0x88,0x00,0x70,0x00,0x00},/*"B",34*/
    {0x03,0xE0,0x0C,0x18,0x10,0x04,0x10,0x04,0x10,0x04,0x10,0x08,0x1C,0x10,0x00,0x00},/*"C",35*/
    {0x10,0x04,0x1F,0xFC,0x10,0x04,0x10,0x04,0x10,0x04,0x08,0x08,0x07,0xF0,0x00,0x00},/*"D",36*/
    {0x10,0x04,0x1F,0xFC,0x11,0x04,0x11,0x04,0x17,0xC4,0x10,0x04,0x08,0x18,0x00,0x00},/*"E",37*/
    {0x10,0x04,0x1F,0xFC,0x11,0x04,0x11,0x00,0x17,0xC0,0x10,0x00,0x08,0x00,0x00,0x00},/*"F",38*/
    {0x03,0xE0,0x0C,0x18,0x10,0x04,0x10,0x04,0x10,0x44,0x1C,0x78,0x00,0x40,0x00,0x00},/*"G",39*/
    {0x10,0x04,0x1F,0xFC,0x10,0x84,0x00,0x80,0x00,0x80,0x10,0x84,0x1F,0xFC,0x10,0x04},/*"H",40*/
    {0x00,0x00,0x10,0x04,0x10,0x04,0x1F,0xFC,0x10,0x04,0x10,0x04,0x00,0x00,0x00,0x00},/*"I",41*/
    {0x00,0x03,0x00,0x01,0x10,0x01,0x10,0x01,0x1F,0xFE,0x10,0x00,0x10,0x00,0x00,0x00},/*"J",42*/
    {0x10,0x04,0x1F,0xFC,0x11,0x04,0x03,0x80,0x14,0x64,0x18,0x1C,0x10,0x04,0x00,0x00},/*"K",43*/
    {0x10,0x04,0x1F,0xFC,0x10,0x04,0x00,0x04,0x00,0x04,0x00,0x04,0x00,0x0C,0x00,0x00},/*"L",44*/
    {0x10,0x04,0x1F,0xFC,0x1F,0x00,0x00,0xFC,0x1F,0x00,0x1F,0xFC,0x10,0x04,0x00,0x00},/*"M",45*/
    {0x10,0x04,0x1F,0xFC,0x0C,0x04,0x03,0x00,0x00,0xE0,0x10,0x18,0x1F,0xFC,0x10,0x00},/*"N",46*/
    {0x07,0xF0,0x08,0x08,0x10,0x04,0x10,0x04,0x10,0x04,0x08,0x08,0x07,0xF0,0x00,0x00},/*"O",47*/
    {0x10,0x04,0x1F,0xFC,0x10,0x84,0x10,0x80,0x10,0x80,0x10,0x80,0x0F,0x00,0x00,0x00},/*"P",48*/
    {0x07,0xF0,0x08,0x18,0x10,0x24,0x10,0x24,0x10,0x1C,0x08,0x0A,0x07,0xF2,0x00,0x00},/*"Q",49*/
    {0x10,0x04,0x1F,0xFC,0x11,0x04,0x11,0x00,0x11,0xC0,0x11,0x30,0x0E,0x0C,0x00,0x04},/*"R",50*/
    {0x00,0x00,0x0E,0x1C,0x11,0x04,0x10,0x84,0x10,0x84,0x10,0x44,0x1C,0x38,0x00,0x00},/*"S",51*/
    {0x18,0x00,0x10,0x00,0x10,0x04,0x1F,0xFC,0x10,0x04,0x10,0x00,0x18,0x00,0x00,0x00},/*"T",52*/
    {0x10,0x00,0x1F,0xF8,0x10,0x04,0x00,0x04,0x00,0x04,0x10,0x04,0x1F,0xF8,0x10,0x00},/*"U",53*/
    {0x10,0x00,0x1E,0x00,0x11,0xE0,0x00,0x1C,0x00,0x70,0x13,0x80,0x1C,0x00,0x10,0x00},/*"V",54*/
    {0x1F,0xC0,0x10,0x3C,0x00,0xE0,0x1F,0x00,0x00,0xE0,0x10,0x3C,0x1F,0xC0,0x00,0x00},/*"W",55*/
    {0x10,0x04,0x18,0x0C,0x16,0x34,0x01,0xC0,0x01,0xC0,0x16,0x34,0x18,0x0C,0x10,0x04},/*"X",56*/
    {0x10,0x00,0x1C,0x00,0x13,0x04,0x00,0xFC,0x13,0x04,0x1C,0x00,0x10,0x00,0x00,0x00},/*"Y",57*/
    {0x08,0x04,0x10,0x1C,0x10,0x64,0x10,0x84,0x13,0x04,0x1C,0x04,0x10,0x18,0x00,0x00},/*"Z",58*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0xFE,0x40,0x02,0x40,0x02,0x40,0x02,0x00,0x00},/*"[",59*/
    {0x00,0x00,0x30,0x00,0x0C,0x00,0x03,0x80,0x00,0x60,0x00,0x1C,0x00,0x03,0x00,0x00},/*"\",60*/
    {0x00,0x00,0x40,0x02,0x40,0x02,0x40,0x02,0x7F,0xFE,0x00,0x00,0x00,0x00,0x00,0x00},/*"]",61*/
    {0x00,0x00,0x00,0x00,0x20,0x00,0x40,0x00,0x40,0x00,0x40,0x00,0x20,0x00,0x00,0x00},/*"^",62*/
    {0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01},/*"_",63*/
    {0x00,0x00,0x40,0x00,0x40,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"`",64*/
    {0x00,0x00,0x00,0x98,0x01,0x24,0x01,0x44,0x01,0x44,0x01,0x44,0x00,0xFC,0x00,0x04},/*"a",65*/
    {0x10,0x00,0x1F,0xFC,0x00,0x88,0x01,0x04,0x01,0x04,0x00,0x88,0x00,0x70,0x00,0x00},/*"b",66*/
    {0x00,0x00,0x00,0x70,0x00,0x88,0x01,0x04,0x01,0x04,0x01,0x04,0x00,0x88,0x00,0x00},/*"c",67*/
    {0x00,0x00,0x00,0x70,0x00,0x88,0x01,0x04,0x01,0x04,0x11,0x08,0x1F,0xFC,0x00,0x04},/*"d",68*/
    {0x00,0x00,0x00,0xF8,0x01,0x44,0x01,0x44,0x01,0x44,0x01,0x44,0x00,0xC8,0x00,0x00},/*"e",69*/
    {0x00,0x00,0x01,0x04,0x01,0x04,0x0F,0xFC,0x11,0x04,0x11,0x04,0x11,0x00,0x18,0x00},/*"f",70*/
    {0x00,0x00,0x00,0xD6,0x01,0x29,0x01,0x29,0x01,0x29,0x01,0xC9,0x01,0x06,0x00,0x00},/*"g",71*/
    {0x10,0x04,0x1F,0xFC,0x00,0x84,0x01,0x00,0x01,0x00,0x01,0x04,0x00,0xFC,0x00,0x04},/*"h",72*/
    {0x00,0x00,0x01,0x04,0x19,0x04,0x19,0xFC,0x00,0x04,0x00,0x04,0x00,0x00,0x00,0x00},/*"i",73*/
    {0x00,0x00,0x00,0x03,0x00,0x01,0x01,0x01,0x19,0x01,0x19,0xFE,0x00,0x00,0x00,0x00},/*"j",74*/
    {0x10,0x04,0x1F,0xFC,0x00,0x24,0x00,0x40,0x01,0xB4,0x01,0x0C,0x01,0x04,0x00,0x00},/*"k",75*/
    {0x00,0x00,0x10,0x04,0x10,0x04,0x1F,0xFC,0x00,0x04,0x00,0x04,0x00,0x00,0x00,0x00},/*"l",76*/
    {0x01,0x04,0x01,0xFC,0x01,0x04,0x01,0x00,0x01,0xFC,0x01,0x04,0x01,0x00,0x00,0xFC},/*"m",77*/
    {0x01,0x04,0x01,0xFC,0x00,0x84,0x01,0x00,0x01,0x00,0x01,0x04,0x00,0xFC,0x00,0x04},/*"n",78*/
    {0x00,0x00,0x00,0xF8,0x01,0x04,0x01,0x04,0x01,0x04,0x01,0x04,0x00,0xF8,0x00,0x00},/*"o",79*/
    {0x01,0x01,0x01,0xFF,0x00,0x85,0x01,0x04,0x01,0x04,0x00,0x88,0x00,0x70,0x00,0x00},/*"p",80*/
    {0x00,0x00,0x00,0x70,0x00,0x88,0x01,0x04,0x01,0x04,0x01,0x05,0x01,0xFF,0x00,0x01},/*"q",81*/
    {0x01,0x04,0x01,0x04,0x01,0xFC,0x00,0x84,0x01,0x04,0x01,0x00,0x01,0x80,0x00,0x00},/*"r",82*/
    {0x00,0x00,0x00,0xCC,0x01,0x24,0x01,0x24,0x01,0x24,0x01,0x24,0x01,0x98,0x00,0x00},/*"s",83*/
    {0x00,0x00,0x01,0x00,0x01,0x00,0x07,0xF8,0x01,0x04,0x01,0x04,0x00,0x00,0x00,0x00},/*"t",84*/
    {0x01,0x00,0x01,0xF8,0x00,0x04,0x00,0x04,0x00,0x04,0x01,0x08,0x01,0xFC,0x00,0x04},/*"u",85*/
    {0x01,0x00,0x01,0x80,0x01,0x70,0x00,0x0C,0x00,0x10,0x01,0x60,0x01,0x80,0x01,0x00},/*"v",86*/
    {0x01,0xF0,0x01,0x0C,0x00,0x30,0x01,0xC0,0x00,0x30,0x01,0x0C,0x01,0xF0,0x01,0x00},/*"w",87*/
    {0x00,0x00,0x01,0x04,0x01,0x8C,0x00,0x74,0x01,0x70,0x01,0x8C,0x01,0x04,0x00,0x00},/*"x",88*/
    {0x01,0x01,0x01,0x81,0x01,0x71,0x00,0x0E,0x00,0x18,0x01,0x60,0x01,0x80,0x01,0x00},/*"y",89*/
    {0x00,0x00,0x01,0x84,0x01,0x0C,0x01,0x34,0x01,0x44,0x01,0x84,0x01,0x0C,0x00,0x00},/*"z",90*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x3E,0xFC,0x40,0x02,0x40,0x02},/*"{",91*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00},/*"|",92*/
    {0x00,0x00,0x40,0x02,0x40,0x02,0x3E,0xFC,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"}",93*/
    {0x00,0x00,0x60,0x00,0x80,0x00,0x80,0x00,0x40,0x00,0x40,0x00,0x20,0x00,0x20,0x00},/*"~",94*/
    }; 
    
    void Motor_Init(void);                        //电机接口初始化
    void forward(void);                           //小车前进控制函数
    void back(void);			                        //小车后退控制函数
    void left_turn(void);	                        //向左转
    void right_turn(void);		                    //向右转  
    void stop(void);			                        //停车
    void circle_left(void);		                    //原地向左转圈
    void circle_right(void);		 	                //原地向右转圈 
    void left_moto(void);                         //左电机调速函数
    void right_moto(void);                        //右电机调速函数
    
    void Timer1_Init(u16 arr,u16 psc);            //定时器1初始化函数
    void Timer2_Init(u16 arr,u16 psc);            //定时器2初始化函数
    void Timer3_Init(u16 arr,u16 psc);            //定时器3初始化函数
    
    void Beep_Init(void);                         //蜂鸣器接口初始化
    void Key_Init(void);                          //按键接口初始化函数
    void Key_Scan(void);                          //按键1扫描函数
    void Bz_Init(void);                           //红外避障信号接口初始化函数
    
    void OLED_WR_Byte(u8 dat,u8 cmd);	    
    void OLED_Display_On(void);
    void OLED_Display_Off(void);
    void OLED_Refresh_Gram(void);		   				   		    
    void Oled_Init(void);
    void OLED_Clear(void);
    void OLED_DrawPoint(u8 x,u8 y,u8 t);
    void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 size,u8 mode);
    void OLED_ShowNumber(u8 x,u8 y,u32 num,u8 len,u8 size);
    void OLED_ShowString(u8 x,u8 y,const u8 *p);	
    
    void Oled_Show(void);
    
    void Dj_Init(void);
    void ControlLeftOrRight(void);
    
    void Hw_bz(void);
    
    void process(void);
    /****************************************************************
                              程序功能
    将超声波模块、舵机安装好并连接好杜邦线。智能小车上电后,按下开发
    板启动按键(靠近电源开关那个)小车开始避障碍运动。同时测到的距离
    值会在OLED中显示出来。
    
    接线说明:
    电机驱动线:
    开发板的PA2接小车底盘的M6
    开发板的PA3接小车底盘的M5
    开发板的PA4接小车底盘的M4
    开发板的PA5接小车底盘的M3
    开发板的PA6接小车底盘的M2
    开发板的PA7接小车底盘的M1
    
    超声波模块接线:
    超声波模块的VCC接开发板的5V
    超声波模块的GND接开发板的GND
    超声波模块的Trig接开发板的PB1
    超声波模块的Echo接开发板的PB0
    
    舵机接线:
    将舵机线接到小车底盘的舵机接口上(注意方向,舵机棕线是GND,红线是VCC,
    橙线是信号线。它们分别对应小车底盘舵机接口上的GND,5V和S)。另外,舵机
    的信号线需要一条单独的杜邦线连接到开发板的PA0。
    
    电源线:
    开发板的5V接小车底盘的5V
    开发板的GND接小车底盘的GND
    
    红外避障信号线:
    开发板的PA13接小车底盘的H3
    开发板的PA14接小车底盘的H4
    (本实验采用红外避障作为超声波避障的辅助,所以红外避障信号线也要
    连接)
    ****************************************************************/
    int main(void)
    {
     u8 k;
    	
     Stm32_Clock_Init(9);                         //系统时钟设置
     delay_init(72);	                            //延时初始化
     JTAG_Set(JTAG_SWD_DISABLE);                  //关闭JTAG接口
     JTAG_Set(SWD_ENABLE);                        //打开SWD接口
     Oled_Init();                                 //OLED初始化  
     Timer3_Init(0XFFFF,72-1);	                  //超声波初始化
     Timer1_Init(499,7199);
     Motor_Init();		  	                        //初始化与电机连接的硬件接口
     Timer2_Init(25,719);                         //10Khz的计数频率,计数到9为1ms
     Beep_Init();                                 //蜂鸣器接口初始化                                 
     Key_Init();                                  //按键接口初始化
     Bz_Init();                                   //红外避障信号接口初始化
     Dj_Init();                                   //舵机控制接口初始化
    	
     ControlLeftOrRight();                        //舵机初始化
    
     Key_Scan();                                  //按键扫描
    	
     while(1)        	                               
     { 
      if(timer>=400)
      {
       timer = 0;
    
       Oled_Show();                               //oled显示提示信息
       S_temp = JuLi;                             //超声波模块测到的距离值
       Hw_bz();                                   //红外避障
    
       if((S_temp>=250)&&(hw_flag == 0))		      //如果不满足避障条件(超声波模块测到前方障碍物距离大于25厘米或红外避障感应到前方有障碍物)     
       {
    	  BEEP = 1;
    	  forward();                                //智能小车继续前进
       }
       else                                       //如果满足避障条件    
       {	   
    	  BEEP = 0;                                 //蜂鸣器响
    	  stop();                                   //智能小车停下
    	  for(k=0;k<=8;k++)
        {
    	   delay_ms(1000); 
        }
    	  BEEP = 1;                                 //蜂鸣器不响
    	  process();                                //避障处理
       }
      }  
      Oled_Show();                                //OLED显示信息
     }	 
    }
    
    void Dj_Init(void)                            //舵机控制接口初始化函数
    {
     RCC->APB2ENR|=1<<2;                          //使能PORTA口时钟 
     GPIOA->CRL&=0XFFFFFFF0;                      //PORTA.0 推挽输出
     GPIOA->CRL|=0X00000003;
     GPIOA->ODR|=1<<0;                            //输出1
    } 
    
    void ControlLeftOrRight(void)                 //舵机自检函数
    {
     u8 i;
     
     LeftOrRight    = Stop;                       //舵机停在中间
     TimeOutCounter = 0;
     for(i=0;i<=9;i++)
     {
    	delay_ms(1000); 
     }
     
     LeftOrRight = Left;                          //舵机左边
     TimeOutCounter = 0;
     for(i=0;i<=9;i++)
     {
    	delay_ms(1000); 
     }
    
     LeftOrRight = Right;                         //舵机右边
     TimeOutCounter = 0;
     for(i=0;i<=13;i++)
     {
    	delay_ms(1000); 
     }
     
     LeftOrRight    = Stop;                       //舵机停在中间
     TimeOutCounter = 0;
     for(i=0;i<=9;i++)
     {
    	delay_ms(1000); 
     }
    }
    
    void Hw_bz(void)                              //红外避障函数
    {
     if((BZ_LEFT == 0)||(BZ_RIGHT == 0))	        //如果左右两边任何一个红外避障模块感应到障碍物    	   
     {
      hw_flag = 1;		                            //设置红外避障标志
     }
     else 
     {
      hw_flag = 0;                                //屏蔽红外避障标志
     }			   
    }
    
    void Beep_Init(void)                          //蜂鸣器接口初始化
    {
     RCC->APB2ENR|=1<<3;                          //使能PORTB时钟
      
     GPIOB->CRL&=0XFF0FFFFF;
     GPIOB->CRL|=0X00300000;                      //PB5推挽输出
     GPIOB->ODR|=1<<5;                            //PB5输出高	
    }
    
    void Bz_Init(void)
    {
     RCC->APB2ENR|=1<<2;                          //使能PORTA时钟	   	 
     GPIOA->CRH&=0XFF0FFFFF; 
     GPIOA->CRH|=0X00800000;                      //PA13上拉输入
     GPIOA->ODR|=1<<13;                           //PA13上拉	
    
     GPIOA->CRH&=0XF0FFFFFF; 
     GPIOA->CRH|=0X08000000;                      //PA14上拉输入
     GPIOA->ODR|=1<<14;                           //PA14上拉	
    }
    
    void Key_Init(void)
    {
     RCC->APB2ENR|=1<<2;                          //使能PORTA时钟	   	 
     GPIOA->CRH&=0XFFFFFFF0; 
     GPIOA->CRH|=0X00000008;                      //PA8上拉输入
     GPIOA->ODR|=1<<8;                            //PA8上拉	
    } 
    
    void Key_Scan(void)                           //按键扫描函数
    {
     LOOP:if(KEY1==0)  		                        //第一次判断是否有按键按下
          {
           delay_ms(5);
    	     if(KEY1==0)  		                      //第二次判断是否有按键按下
           {
    		    BEEP = 0;		                          //蜂鸣器响
    		    while(KEY1 == 0);                     //等待按键松开
    		    BEEP = 1;	                            //蜂鸣器不响
           }
    	     else
    	     {
    	      goto LOOP;                            //第一次判断时如果按键没有按下重新扫描
    	     }
          }
          else
          { 
           goto LOOP;	                            //第二次判断时如果按键没有按下重新扫描
          }
    }
    
    void Motor_Init(void)
    {
     RCC->APB2ENR|=1<<2;                    //使能PORTA接口时钟
      
     GPIOA->CRL&=0XFFFFF0FF;
     GPIOA->CRL|=0X00000300;                //PA2推挽输出
     GPIOA->ODR|=1<<2;                      //PA2输出高电平
    	
     GPIOA->CRL&=0XFFFF0FFF;
     GPIOA->CRL|=0X00003000;                //PA3推挽输出
     GPIOA->ODR|=1<<3;                      //PA3输出高电平
    	
     GPIOA->CRL&=0XFFF0FFFF;
     GPIOA->CRL|=0X00030000;                //PA4推挽输出
     GPIOA->ODR|=1<<4;                      //PA4输出高电平
    
     GPIOA->CRL&=0XFF0FFFFF;
     GPIOA->CRL|=0X00300000;                //PA5推挽输出
     GPIOA->ODR|=1<<5;                      //PA5输出高电平
    	
     GPIOA->CRL&=0XF0FFFFFF;
     GPIOA->CRL|=0X03000000;                //PA6推挽输出
     GPIOA->ODR|=1<<6;                      //PA6输出高电平	
    
     GPIOA->CRL&=0X0FFFFFFF;
     GPIOA->CRL|=0X30000000;                //PA7推挽输出
     GPIOA->ODR|=1<<7;                      //PA7输出高电平
    }
    
    void forward(void)                      //小车前进控制函数
    {
     IN1 = 1;
     IN2 = 0;
     
     IN3 = 1;
     IN4 = 0;  
    }
    
    void back(void)			                    //小车后退控制函数
    { 
     IN1 = 0;
     IN2 = 1;
     
     IN3 = 0;
     IN4 = 1; 
    }
    
    void left_turn(void)	                  //向左转
    {  
     IN1 = 1;
     IN2 = 0; 
    
     IN3 = 0;
     IN4 = 0;
    }
    
    void right_turn(void)		                //向右转  
    { 
     IN1 = 0;
     IN2 = 0;
     
     IN3 = 1;
     IN4 = 0;    
    }
    
    void stop(void)			                    //停车
    { 
     IN1 = 0;
     IN2 = 0;   
     
     IN3 = 0;
     IN4 = 0; 
    }
    
    void circle_left(void)		              //原地向左转圈
    { 
     IN1 = 1;
     IN2 = 0; 
     
     IN3 = 0;
     IN4 = 1; 
    }
    
    void circle_right(void)		 	            //原地向右转圈 
    {
     IN1 = 0;
     IN2 = 1;    
     
     IN3 = 1;
     IN4 = 0;    
    }
    
    void left_moto(void)                    //左电机调速函数
    {  
     if(left_pwm)
     {
      if(pwmval_left <= pwmval_left_init)
      {
       EN1 = 1; 
      }
      else 
      {
       EN1 = 0;
      }
      if(pwmval_left >= 20)
      {
       pwmval_left = 0;
      }
     }
     else    
     {
      EN1 = 0;                      
     }
    }
    
    void right_moto(void)                   //右电机调速函数
    { 
     if(right_pwm)
     { 
      if(pwmval_right <= pwmval_right_init)		  
      {
       EN2 = 1; 							   
      }
      else if(pwmval_right > pwmval_right_init)
      {
       EN2 = 0;
      }
      if(pwmval_right >= 20)
      {
       pwmval_right = 0;
      }
     }
     else    
     {
      EN2 = 0;	                                  	    
     }
    }
    
    void OLED_Refresh_Gram(void)
    {
     u8 i,n;		    
     for(i=0;i<8;i++)  
     {  
      OLED_WR_Byte (0xb0+i,OLED_CMD);       //设置页地址(0~7)
      OLED_WR_Byte (0x00,OLED_CMD);         //设置显示位置—列低地址
      OLED_WR_Byte (0x10,OLED_CMD);         //设置显示位置—列高地址   
      for(n=0;n<128;n++)
      {
       OLED_WR_Byte(OLED_GRAM[n][i],OLED_DATA); 
      }
     }   
    }
    
    void OLED_WR_Byte(u8 dat,u8 cmd)
    {	
     u8 i;			  
     if(cmd)
     {
      OLED_RS_Set();
     }
     else 
     {
      OLED_RS_Clr();
     }		  
     for(i=0;i<8;i++)
     {			  
      OLED_SCLK_Clr();
      if(dat&0x80)
      {
       OLED_SDIN_Set();
      }
      else
      { 
       OLED_SDIN_Clr();
      }
      OLED_SCLK_Set();
      dat<<=1;   
     }				 		  
     OLED_RS_Set();   	  
    }
    
    void OLED_Display_On(void)
    {
     OLED_WR_Byte(0X8D,OLED_CMD);            //SET DCDC命令
     OLED_WR_Byte(0X14,OLED_CMD);            //DCDC ON
     OLED_WR_Byte(0XAF,OLED_CMD);            //DISPLAY ON
    }
    
    void OLED_Display_Off(void)
    {
     OLED_WR_Byte(0X8D,OLED_CMD);            //SET DCDC命令
     OLED_WR_Byte(0X10,OLED_CMD);            //DCDC OFF
     OLED_WR_Byte(0XAE,OLED_CMD);            //DISPLAY OFF
    }
    
    void OLED_Clear(void)  
    {  
     u8 i,n;  
     for(i=0;i<8;i++)for(n=0;n<128;n++)OLED_GRAM[n][i]=0X00;  
     OLED_Refresh_Gram();                    //更新显示
    }
    
    void OLED_DrawPoint(u8 x,u8 y,u8 t)
    {
     u8 pos,bx,temp=0;
     if(x>127||y>63)return;                  //超出范围了.
     pos=7-y/8;
     bx=y%8;
     temp=1<<(7-bx);
     if(t)OLED_GRAM[x][pos]|=temp;
     else OLED_GRAM[x][pos]&=~temp;	    
    }
    
    void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 size,u8 mode)
    {      			    
     u8 temp,t,t1;
     u8 y0=y;
     chr=chr-' ';                             //得到偏移后的值				   
     for(t=0;t<size;t++)
     {   
      if(size==12)			                      //调用1206字体
      {
       temp=oled_asc2_1206[chr][t];  
      }
      else 							                      //调用1608字体
      {
       temp=oled_asc2_1608[chr][t];		  
      }	                          
      for(t1=0;t1<8;t1++)
      {
       if(temp&0x80)
       {
        OLED_DrawPoint(x,y,mode);
       }
       else
       {
        OLED_DrawPoint(x,y,!mode);
       }
       temp<<=1;
       y++;
       if((y-y0)==size)
       {
    	y=y0;
    	x++;
    	break;
       }
      }  	 
     }          
    }
    
    u32 oled_pow(u8 m,u8 n)
    {
     u32 result=1;	 
     while(n--)result*=m;    
     return result;
    }
    
    void OLED_ShowNumber(u8 x,u8 y,u32 num,u8 len,u8 size)
    {         	
     u8 t,temp;
     u8 enshow=0;						   
     for(t=0;t<len;t++)
     {
      temp=(num/oled_pow(10,len-t-1))%10;
      if(enshow==0&&t<(len-1))
      {
       if(temp==0)
       {
    	OLED_ShowChar(x+(size/2)*t,y,' ',size,1);
        continue;
       }
       else
       {
        enshow=1;
       } 		 	 
      }
      OLED_ShowChar(x+(size/2)*t,y,temp+'0',size,1); 
     }
    }
    
    void OLED_ShowString(u8 x,u8 y,const u8 *p)
    {
     #define MAX_CHAR_POSX 122
     #define MAX_CHAR_POSY 58          
     while(*p!='\0')
     {       
      if(x>MAX_CHAR_POSX){x=0;y+=16;}
      if(y>MAX_CHAR_POSY){y=x=0;OLED_Clear();}
      OLED_ShowChar(x,y,*p,12,1);	 
      x+=8;
      p++;
     }  
    }
    
    void Oled_Init(void)
    { 	  	 
     RCC->APB2ENR|=1<<3;                  //使能PORTB时钟	   	 
     GPIOB->CRL&=0XFFF0FFFF; 
     GPIOB->CRL|=0X00020000;              //PB4 推挽输出   
    
     RCC->APB2ENR|=1<<4;     	            //使能PORTC时钟  
     RCC->APB2ENR|=1<<0;     	            //使能AFIO时钟	
     GPIOC->CRH&=0X000FFFFF;	            //PC13,14,15设置成输出 2MHz 推挽输出   
     GPIOC->CRH|=0X22200000; 
     PWR->CR|=1<<8;	                      //取消备份区写保护 
     RCC->BDCR&=0xFFFFFFFE;	              //外部低俗振荡器关闭 PC14,PC15成为普通IO	 	
     BKP->CR&=0xFFFFFFFE; 	              //侵入检测TAMPER引脚作为通用IO口使用 
     PWR->CR&=0xFFFFFEFF;	                //备份区写保护
    
     OLED_RST_Clr();
     delay_ms(100);
     OLED_RST_Set(); 
    					  
     OLED_WR_Byte(0xAE,OLED_CMD);         //关闭显示
     OLED_WR_Byte(0xD5,OLED_CMD);         //设置时钟分频因子,震荡频率
     OLED_WR_Byte(80,OLED_CMD);           //[3:0],分频因子;[7:4],震荡频率
     OLED_WR_Byte(0xA8,OLED_CMD);         //设置驱动路数
     OLED_WR_Byte(0X3F,OLED_CMD);         //默认0X3F(1/64) 
     OLED_WR_Byte(0xD3,OLED_CMD);         //设置显示偏移
     OLED_WR_Byte(0X00,OLED_CMD);         //默认为0
    
     OLED_WR_Byte(0x40,OLED_CMD);         //设置显示开始行 [5:0],行数.
    													    
     OLED_WR_Byte(0x8D,OLED_CMD);         //电荷泵设置
     OLED_WR_Byte(0x14,OLED_CMD);         //bit2,开启/关闭
     OLED_WR_Byte(0x20,OLED_CMD);         //设置内存地址模式
     OLED_WR_Byte(0x02,OLED_CMD);         //[1:0],00,列地址模式;01,行地址模式;10,页地址模式;默认10;
     OLED_WR_Byte(0xA1,OLED_CMD);         //段重定义设置,bit0:0,0->0;1,0->127;
     OLED_WR_Byte(0xC0,OLED_CMD);         //设置COM扫描方向;bit3:0,普通模式;1,重定义模式 COM[N-1]->COM0;N:驱动路数
     OLED_WR_Byte(0xDA,OLED_CMD);         //设置COM硬件引脚配置
     OLED_WR_Byte(0x12,OLED_CMD);         //[5:4]配置
    		 
     OLED_WR_Byte(0x81,OLED_CMD);         //对比度设置
     OLED_WR_Byte(0xEF,OLED_CMD);         //1~255;默认0X7F (亮度设置,越大越亮)
     OLED_WR_Byte(0xD9,OLED_CMD);         //设置预充电周期
     OLED_WR_Byte(0xf1,OLED_CMD);         //[3:0],PHASE 1;[7:4],PHASE 2;
     OLED_WR_Byte(0xDB,OLED_CMD);         //设置VCOMH 电压倍率
     OLED_WR_Byte(0x30,OLED_CMD);         //[6:4] 000,0.65*vcc;001,0.77*vcc;011,0.83*vcc;
    
     OLED_WR_Byte(0xA4,OLED_CMD);         //全局显示开启;bit0:1,开启;0,关闭;(白屏/黑屏)
     OLED_WR_Byte(0xA6,OLED_CMD);         //设置显示方式;bit0:1,反相显示;0,正常显示	    						   
     OLED_WR_Byte(0xAF,OLED_CMD);         //开启显示	 
     OLED_Clear();
    } 
    
    void Oled_Show(void)
    {	
     OLED_ShowString(20,10,"ChaoSB-TEST");                             
     
     OLED_ShowString(0,30,"Distance:");
     OLED_ShowNumber(80,30,(u16)JuLi,4,12);
     OLED_ShowString(110,30,"mm");
    	
     OLED_Refresh_Gram();	                        //刷新
    }
    
    void process(void)                            //避障处理函数
    {
     u8 j;
     u8 p;
    	
     u32 S1_temp = 0;
     u32 S2_temp = 0;
     	 
     LeftOrRight    = Left;                       //舵机转到左边
     TimeOutCounter = 0;
     for(j=0;j<=9;j++)
     {
    	delay_ms(1000); 
     }
    
     Oled_Show();                                 //显示障碍物距离信息
     S1_temp = JuLi;                              //小车左边障碍物距离存在变量S1_temp中
      
     LeftOrRight    = Right;                      //舵机转到右边
     TimeOutCounter = 0;
     for(j=0;j<=13;j++)
     {
    	delay_ms(1000); 
     }
     
     Oled_Show();                                 //显示障碍物距离信息
     S2_temp = JuLi; 	                            //小车右边障碍物距离存在变量S2_temp中
    	
     LeftOrRight    = Stop;                       //舵机回到中间
     TimeOutCounter = 0;
     for(j=0;j<=9;j++)
     {
    	delay_ms(1000); 
     }
    
     if((S1_temp<=200)||(S2_temp<=200))           //如果超声波模块测得左边或右边障碍物的距离小于20厘米
     {
      back();		                                  //小车后退
      for(p=0;p<=10;p++)
      {
    	 delay_ms(1000); 
      }
     }
     else
     {
      back();		                                  //小车后退
      for(p=0;p<=8;p++)
      {
    	 delay_ms(1000); 
      }   
     }
    
     stop();                                      //小车停下
     for(p=0;p<=2;p++)
     {
    	delay_ms(1000); 
     } 
     		   
     if(S1_temp>S2_temp)		                     //如果左边障碍物离小车远
     {
      circle_left(); 	                           //小车向左转
      for(p=0;p<=2;p++)
      {
    	 delay_ms(1000); 
      }
     }				      
     else if(S1_temp<S2_temp)                    //如果右边障碍物离小车远
     {
      circle_right();		                         //小车向右转
      for(p=0;p<=2;p++)
      {
    	 delay_ms(1000); 
      }
     }
    }
    
    void Timer3_Init(u16 arr,u16 psc)	
    {	 
     RCC->APB1ENR|=1<<1;           //TIM3时钟使能     
     RCC->APB2ENR|=1<<3;    	     //使能PORTB时钟   	 
     GPIOB->CRL&=0XFFFFFF00; 
     GPIOB->CRL|=0X00000028;       //PB.0 输入 PB.1输出
    	
     TIM3->ARR=arr;  		           //设定计数器自动重装值   
     TIM3->PSC=psc;  		           //预分频器 
     TIM3->CCMR2|=1<<0;	           //选择输入端 
     TIM3->CCMR2|=0<<4; 	         //配置输入滤波器 不滤波
     TIM3->CCMR2|=0<<2; 	         //配置输入分频,不分频 
    
     TIM3->CCER|=0<<9; 	           //上升沿捕获
     TIM3->CCER|=1<<8; 	           //允许捕获计数器的值到捕获寄存器中
    
     TIM3->DIER|=1<<3;             //允许捕获中断				
     TIM3->DIER|=1<<0;             //允许更新中断	
     TIM3->CR1|=0x01;              //使能定时器3
     MY_NVIC_Init(1,3,TIM3_IRQChannel,1);
    }
    
    /**************************************************************************
    函数功能:超声波接收回波函数
    入口参数:无
    返回  值:无
    **************************************************************************/
    u16 TIM3CH3_CAPTURE_STA,TIM3CH3_CAPTURE_VAL;
    void Read_Distane(void)
    {   
     PBout(1)=1;
     delay_us(15); 
     PBout(1)=0;	
     if(TIM3CH3_CAPTURE_STA&0X80)               //成功捕获到了一次高电平
     {
      JuLi=TIM3CH3_CAPTURE_STA&0X3F;
      JuLi*=65536;					                    //溢出时间总和
      JuLi+=TIM3CH3_CAPTURE_VAL;		            //得到总的高电平时间
      JuLi=JuLi*170/1000;
      TIM3CH3_CAPTURE_STA=0;			              //开启下一次捕获
     }				
    }
    
    /**************************************************************************
    函数功能:超声波回波脉宽读取中断
    入口参数:无
    返回  值:无
    **************************************************************************/
    void TIM3_IRQHandler(void)
    { 		    		  			    
     u16 tsr;
     tsr=TIM3->SR;
     if((TIM3CH3_CAPTURE_STA&0X80)==0)           //还未成功捕获	
     {
      if(tsr&0X01)                               //溢出
      {	    
       if(TIM3CH3_CAPTURE_STA&0X40)              //已经捕获到高电平了
       {
    	  if((TIM3CH3_CAPTURE_STA&0X3F)==0X3F)     //高电平太长了
    	  {
    	   TIM3CH3_CAPTURE_STA|=0X80;              //标记成功捕获了一次
    	   TIM3CH3_CAPTURE_VAL=0XFFFF;
    	  }
    	  else 
    	  {
    	   TIM3CH3_CAPTURE_STA++;
    	  }
       }	 
      }
      if(tsr&0x08)                               //捕获3发生捕获事件
      {	
       if(TIM3CH3_CAPTURE_STA&0X40)		           //捕获到一个下降沿 		
       {	  			
    	  TIM3CH3_CAPTURE_STA|=0X80;		           //标记成功捕获到一次高电平脉宽
    	  TIM3CH3_CAPTURE_VAL=TIM3->CCR3;	         //获取当前的捕获值.
    	  TIM3->CCER&=~(1<<9);			               //CC1P=0 设置为上升沿捕获
       }
       else  								                     //还未开始,第一次捕获上升沿
       {
    	  TIM3CH3_CAPTURE_STA=0;			             //清空
    	  TIM3CH3_CAPTURE_VAL=0;
    	  TIM3CH3_CAPTURE_STA|=0X40;		           //标记捕获到了上升沿
    	  TIM3->CNT=0;					                   //计数器清空
    	  TIM3->CCER|=1<<9; 				               //CC1P=1 设置为下降沿捕获
       }		    
      }			     	    					   
     }
     TIM3->SR=0;                                 //清除中断标志位 	     
    }
    
    /**************************************************************************
    函数功能:定时中断初始化
    入口参数:arr:自动重装值  psc:时钟预分频数 
    返回  值:无
    **************************************************************************/
    void Timer1_Init(u16 arr,u16 psc)  
    {  
     RCC->APB2ENR|=1<<11;                       //TIM1时钟使能    
     TIM1->ARR=arr;                             //设定计数器自动重装值   
     TIM1->PSC=psc;                             //预分频器7200,得到10Khz的计数时钟
     TIM1->DIER|=1<<0;                          //允许更新中断				
     TIM1->DIER|=1<<6;                          //允许触发中断	   
     TIM1->CR1|=0x01;                           //使能定时器
     MY_NVIC_Init(1,3,TIM1_UP_IRQChannel,2);
    } 
    
    /**************************************************************************
    函数功能:定时中断初始化
    入口参数:arr:自动重装值  psc:时钟预分频数 
    返回  值:无
    **************************************************************************/
    int TIM1_UP_IRQHandler(void)  
    {    
     if(TIM1->SR&0X0001)                    //5ms定时中断
     {   
      TIM1->SR&=~(1<<0);                    //清除定时器1中断标志位		                        
     }
     Read_Distane();     	
     return 0;
    }
    
    //定时器3中断服务程序	 
    void TIM2_IRQHandler(void)
    { 		    		  			    
     if(TIM2->SR&0X0001)                          //溢出中断
     {
      TimeOutCounter ++;
      timer++;
    
      switch(LeftOrRight)
      {
       case 0 :	                                  //1.5ms
       {
        if(TimeOutCounter<=6)
        {
    	   ControlPort = 1;
        }
        else 
        {		
         ControlPort = 0;
        }
       }break;
    
       case 1 :                                   //1ms
       {
        if(TimeOutCounter<=4)
        {
    	   ControlPort = 1;
        }
        else 
        {
    	   ControlPort = 0;
        }		
       }break;
    
       case 2 :                                   //2ms
       {
        if(TimeOutCounter<=8)
        {
    	   ControlPort = 1;
        }
        else 
        {
    	   ControlPort = 0;
        }			
       }break;
       default : break;
      }
    
      if(TimeOutCounter <= 30)
      {
       EN1 = 1;
       EN2 = 1;
      }
      else if(TimeOutCounter > 30)
      {
       EN1 = 0;
       EN2 = 0;
      }
    	
      if(TimeOutCounter==80)	                    //20ms
      {
       TimeOutCounter = 0;
      } 
     }				   
     TIM2->SR&=~(1<<0);                           //清除中断标志位 	    
    }
    
    //通用定时器中断初始化
    //这里时钟选择为APB1的2倍,而APB1为36M
    //arr:自动重装值。
    //psc:时钟预分频数
    //这里使用的是定时器3
    void Timer2_Init(u16 arr,u16 psc)
    {
     RCC->APB1ENR|=1<<0;                    //TIM2时钟使能    
     TIM2->ARR=arr;                         //设定计数器自动重装值//刚好1ms    
     TIM2->PSC=psc;                         //预分频器7200,得到10Khz的计数时钟
     //这两个东东要同时设置才可以使用中断
     TIM2->DIER|=1<<0;                      //允许更新中断				  
     TIM2->CR1|=0x01;                       //使能定时器3
     MY_NVIC_Init(1,3,TIM2_IRQChannel,2);   //抢占1,子优先级3,组2									 
    }
    
    

    中断函数

    #include "sys.h"
      
    //设置向量表偏移地址
    //NVIC_VectTab:基址
    //Offset:偏移量
    //CHECK OK
    //091207
    void MY_NVIC_SetVectorTable(u32 NVIC_VectTab, u32 Offset)	 
    { 
     //检查参数合法性
     assert_param(IS_NVIC_VECTTAB(NVIC_VectTab));
     assert_param(IS_NVIC_OFFSET(Offset));  	 
     SCB->VTOR = NVIC_VectTab|(Offset & (u32)0x1FFFFF80);             //设置NVIC的向量表偏移寄存器
     //用于标识向量表是在CODE区还是在RAM区
    }
    
    //设置NVIC分组
    //NVIC_Group:NVIC分组 0~4 总共5组 
    //CHECK OK
    //091209
    void MY_NVIC_PriorityGroupConfig(u8 NVIC_Group)	 
    { 
     u32 temp,temp1;	  
     temp1=(~NVIC_Group)&0x07;                                       //取后三位
     temp1<<=8;
     temp=SCB->AIRCR;                                                //读取先前的设置
     temp&=0X0000F8FF;                                               //清空先前分组
     temp|=0X05FA0000;                                               //写入钥匙
     temp|=temp1;	   
     SCB->AIRCR=temp;                                                //设置分组	    	  				   
    }
    
    //设置NVIC 
    //NVIC_PreemptionPriority:抢占优先级
    //NVIC_SubPriority       :响应优先级
    //NVIC_Channel           :中断编号
    //NVIC_Group             :中断分组 0~4
    //注意优先级不能超过设定的组的范围!否则会有意想不到的错误
    //组划分:
    //组0:0位抢占优先级,4位响应优先级
    //组1:1位抢占优先级,3位响应优先级
    //组2:2位抢占优先级,2位响应优先级
    //组3:3位抢占优先级,1位响应优先级
    //组4:4位抢占优先级,0位响应优先级
    //NVIC_SubPriority和NVIC_PreemptionPriority的原则是,数值越小,越优先
    //CHECK OK
    //100329
    void MY_NVIC_Init(u8 NVIC_PreemptionPriority,u8 NVIC_SubPriority,u8 NVIC_Channel,u8 NVIC_Group)	 
    { 
     u32 temp;	
     u8 IPRADDR=NVIC_Channel/4;                                    //每组只能存4个,得到组地址 
     u8 IPROFFSET=NVIC_Channel%4;                                  //在组内的偏移
     IPROFFSET=IPROFFSET*8+4;                                      //得到偏移的确切位置
     MY_NVIC_PriorityGroupConfig(NVIC_Group);                      //设置分组
     temp=NVIC_PreemptionPriority<<(4-NVIC_Group);	  
     temp|=NVIC_SubPriority&(0x0f>>NVIC_Group);
     temp&=0xf;                                                    //取低四位
    
     if(NVIC_Channel<32)NVIC->ISER[0]|=1<<NVIC_Channel;            //使能中断位(要清除的话,相反操作就OK)
     else NVIC->ISER[1]|=1<<(NVIC_Channel-32);    
     NVIC->IPR[IPRADDR]|=temp<<IPROFFSET;                         //设置响应优先级和抢断优先级   	    	  				   
    }
    
    //外部中断配置函数
    //只针对GPIOA~G;不包括PVD,RTC和USB唤醒这三个
    //参数:GPIOx:0~6,代表GPIOA~G;BITx:需要使能的位;TRIM:触发模式,1,下升沿;2,上降沿;3,任意电平触发
    //该函数一次只能配置1个IO口,多个IO口,需多次调用
    //该函数会自动开启对应中断,以及屏蔽线   
    //待测试...
    void Ex_NVIC_Config(u8 GPIOx,u8 BITx,u8 TRIM) 
    {
     u8 EXTADDR;
     u8 EXTOFFSET;
     EXTADDR=BITx/4;                                              //得到中断寄存器组的编号
     EXTOFFSET=(BITx%4)*4;
    
     RCC->APB2ENR|=0x01;                                          //使能io复用时钟
    
     AFIO->EXTICR[EXTADDR]&=~(0x000F<<EXTOFFSET);                 //清除原来设置!!!
     AFIO->EXTICR[EXTADDR]|=GPIOx<<EXTOFFSET;                     //EXTI.BITx映射到GPIOx.BITx
    	
     //自动设置
     EXTI->IMR|=1<<BITx;                                          //开启line BITx上的中断
     if(TRIM&0x01)EXTI->FTSR|=1<<BITx;                            //line BITx上事件下降沿触发
     if(TRIM&0x02)EXTI->RTSR|=1<<BITx;                            //line BITx上事件上升降沿触发
    } 
    
    //不能在这里执行所有外设复位!否则至少引起串口不工作.		    
    //把所有时钟寄存器复位
    //CHECK OK
    //091209
    void MYRCC_DeInit(void)
    {										  					   
     RCC->APB1RSTR = 0x00000000;//复位结束			 
     RCC->APB2RSTR = 0x00000000; 
    	  
     RCC->AHBENR = 0x00000014;  //睡眠模式闪存和SRAM时钟使能.其他关闭.	  
     RCC->APB2ENR = 0x00000000; //外设时钟关闭.			   
     RCC->APB1ENR = 0x00000000;   
     RCC->CR |= 0x00000001;     //使能内部高速时钟HSION	 															 
     RCC->CFGR &= 0xF8FF0000;   //复位SW[1:0],HPRE[3:0],PPRE1[2:0],PPRE2[2:0],ADCPRE[1:0],MCO[2:0]					 
     RCC->CR &= 0xFEF6FFFF;     //复位HSEON,CSSON,PLLON
     RCC->CR &= 0xFFFBFFFF;     //复位HSEBYP	   	  
     RCC->CFGR &= 0xFF80FFFF;   //复位PLLSRC, PLLXTPRE, PLLMUL[3:0] and USBPRE 
     RCC->CIR = 0x00000000;     //关闭所有中断
     
     //配置向量表				  
     #ifdef  VECT_TAB_RAM
    	 MY_NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0);
     #else   
    	 MY_NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);
     #endif
    }
    
    //THUMB指令不支持汇编内联
    //采用如下方法实现执行汇编指令WFI
    //CHECK OK
    //091209
    __asm void WFI_SET(void)
    {
     WFI;    
    }
    
    //进入待机模式	 
    //check ok 
    //091202
    void Sys_Standby(void)
    {
     SCB->SCR|=1<<2;                 //使能SLEEPDEEP位 (SYS->CTRL)	   
     RCC->APB1ENR|=1<<28;            //使能电源时钟	    
     PWR->CSR|=1<<8;                 //设置WKUP用于唤醒
     PWR->CR|=1<<2;                  //清除Wake-up 标志
     PWR->CR|=1<<1;                  //PDDS置位		  
     WFI_SET();				               //执行WFI指令		 
    }	  
        
    //系统软复位
    //CHECK OK
    //091209
    void Sys_Soft_Reset(void)
    {   
     SCB->AIRCR =0X05FA0000|(u32)0x04;	  
    } 
    
    //JTAG模式设置,用于设置JTAG的模式
    //mode:jtag,swd模式设置;00,全使能;01,使能SWD;10,全关闭;
    //CHECK OK	
    //100818		  
    void JTAG_Set(u8 mode)
    {
     u32 temp;
     temp=mode;
     temp<<=25;
     RCC->APB2ENR|=1<<0;              //开启辅助时钟	   
     AFIO->MAPR&=0XF8FFFFFF;          //清除MAPR的[26:24]
     AFIO->MAPR|=temp;                //设置jtag模式
    } 
    
    //系统时钟初始化函数
    //pll:选择的倍频数,从2开始,最大值为16	
    //CHECK OK
    //091209
    void Stm32_Clock_Init(u8 PLL)
    {
     unsigned char temp=0;   
     MYRCC_DeInit();		              //复位并配置向量表
     RCC->CR|=0x00010000;             //外部高速时钟使能HSEON
     while(!(RCC->CR>>17));           //等待外部时钟就绪
     RCC->CFGR=0X00000400;            //APB1=DIV2;APB2=DIV1;AHB=DIV1;
     PLL-=2;//抵消2个单位
     RCC->CFGR|=PLL<<18;              //设置PLL值 2~16
     RCC->CFGR|=1<<16;	              //PLLSRC ON 
     FLASH->ACR|=0x32;	              //FLASH 2个延时周期
    
     RCC->CR|=0x01000000;             //PLLON
     while(!(RCC->CR>>25));           //等待PLL锁定
     RCC->CFGR|=0x00000002;           //PLL作为系统时钟	 
     while(temp!=0x02)                //等待PLL作为系统时钟设置成功
     {   
    	temp=RCC->CFGR>>2;
    	temp&=0x03;
     }    
    }		    
    
    

    延时函数

    #include <stm32f10x_map.h>
    #include <stm32f10x_nvic.h>
    #include "delay.h"
     
    static u8  fac_us=0;                     //us延时倍乘数
    static u16 fac_ms=0;                     //ms延时倍乘数
    
    //初始化延迟函数
    //SYSTICK的时钟固定为HCLK时钟的1/8
    //SYSCLK:系统时钟
    void delay_init(u8 SYSCLK)
    {
     SysTick->CTRL&=0xfffffffb;              //bit2清空,选择外部时钟  HCLK/8
     fac_us=SYSCLK/8;		    
     fac_ms=(u16)fac_us*1000;
    }	
    
    //延时nms
    //注意nms的范围,nms<=1864 
    void delay_ms(u16 nms)
    {	 		  	  
     u32 temp;		   
     SysTick->LOAD=(u32)nms*fac_ms;         //时间加载(SysTick->LOAD为24bit)
     SysTick->VAL =0x00;                    //清空计数器
     SysTick->CTRL=0x01 ;                   //开始倒数  
     do
     {
    	temp=SysTick->CTRL;
     }
     while(temp&0x01&&!(temp&(1<<16)));     //等待时间到达   
     SysTick->CTRL=0x00;                    //关闭计数器
     SysTick->VAL =0X00;                    //清空计数器	  	    
    }   
    
    //延时nus
    //nus为要延时的us数.		    								   
    void delay_us(u32 nus)
    {		
     u32 temp;	    	 
     SysTick->LOAD=nus*fac_us;              //时间加载	  		 
     SysTick->VAL=0x00;                     //清空计数器
     SysTick->CTRL=0x01 ;                   //开始倒数 	 
     do
     {
    	temp=SysTick->CTRL;
     }
     while(temp&0x01&&!(temp&(1<<16)));     //等待时间到达   
     SysTick->CTRL=0x00;                    //关闭计数器
     SysTick->VAL =0X00;                    //清空计数器	 
    }
    
    

    接线说明:

    电机驱动线:
    1、 开发板 PA2 接小车底盘 M6 2、 开发板 PA3 接小车底盘 M5
    3、 开发板 PA4 接小车底盘 M4 4、 开发板 PA5 接小车底盘 M3
    5、 开发板 PA6 接小车底盘 M2 6、 开发板 PA7 接小车底盘 M1
    超声波模块接线:
    1、超声波模块 VCC 接开发板 5V 2、超声波模块 GND 接开发板 GND
    3、超声波模块 Trig 接开发板 PB1 4、超声波模块 Echo 接开发板 PB0
    舵机接线:
    将舵机线接到小车底盘的舵机接口上(注意方向, 舵机棕线是 GND,
    红线是 VCC, 橙线是信号线。 它们分别对应小车底盘舵机接口上的
    GND,5V 和 S)。 另外, 舵机的信号线需要一条单独的杜邦线连接到
    开发板的 PA0。
    电源线:
    1、 开发板 5V 接小车底盘 5V 2、 开发板 GND 接小车底盘 GND
    红外避障信号线:
    1、 开发板 PA13 接小车底盘 H3 2、 开发板 PA14 接小车底盘 H4
    ![(本实验采用红外避障作为超声波避障的辅助, 所以红外避障信号线
    也要连接)

    在这里插入图片描述

    小车底板电路图:

    在这里插入图片描述

    展开全文
  • 基于STM32三路超声波避障小车

    万次阅读 多人点赞 2019-08-03 17:40:18
    一般学习单片机的第一步就是做智能小车,通过PWM控制调节车速,以及使用超声波模块进行输入捕获,而这篇文章在于多通道实现输入捕获,因为用到了3个超声波。详细讲解了输入捕获的原理以及一些重要功能的实现代码
  • stm32小车红外循迹+超声波避障停车,基于stm32超声波避障小车,C,C++源码.rar
  • stm32小车红外循迹+超声波避障停车,基于stm32超声波避障小车,C,C++源码.zip
  • 不管是对于初学者还是对于一个玩过单片机的电子爱好者来说,或多或少都接触到过小车项目,今天给大家介绍的的一个项目基于STM32超声波避障小车。这也是我曾经的一个课设,在此开源分享给大家,全文5000多字,干货满...
  • 蓝牙HC-06模块使用STM32 DMA控制器,使小车在循迹+避障+遥控功能的切换中运行流畅
  • 舵机+超声波简介一.SG90舵机二.超声波数据处理 补充一下上一篇博客遗漏掉的一个问题,一般电机的PWM都是有一些频率限制的,而我的直流电机是10Khz来进行驱动的。 一.SG90舵机 这个是我买的舵机+超声波模块,这个...
  • 智能小车使用红外探头避障,适应不同颜色标志
  • 基于stm32f103vc的智能小车源码,运用超声波传感器HC-SR04实现避障,实现控制CDS5516舵机的旋转。
  • 基于stm32f103vc的智能小车——超声波避障部分

    千次阅读 多人点赞 2019-07-10 11:41:14
    在硬件综合训练这门课程中,我们以小组的形式完成了基于stm32f103vc的智能小车的制作,实现的主要功能有:遥控、避障、语音控制、人脸识别以及舵机控制摄像头旋转。其中我主要负责的是stm32板的开发,以下是超声波...
  • 这个程序是基于STM32F103的循迹避障小车,需要的硬件有小车模型、电机驱动模块,直流电机、stm32F103芯片、还有两个红外传感器和一个红外对管
  • stm32的小车红外寻迹加超声波避障,使用oled(spi)显示,用的stm32的最小系统板
  • 本代码基于STM32F103ZET6单片机,使用一个超声波传感器,四个万向轮,四轮驱动,实现小车自主换道避障

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 464
精华内容 185
关键字:

基于stm32的超声波避障