精华内容
下载资源
问答
  • stm32超声波避障小车

    2020-12-28 21:55:38
    舵机+超声波避障小车
  • stm32 超声波避障小车2

    2017-12-14 18:59:09
    stm32 超声波避障小车2stm32 超声波避障小车2stm32 超声波避障小车2stm32 超声波避障小车2
  • stm32 超声波避障小车1

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

    23ba6506c195cf06f74ed4bc2d93f8dd.png

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

    6b8867a49202ae481d953c63524656f4.png
    基于STM32超声波避障小车https://www.zhihu.com/video/1229107254572007424

    处理器电路设计

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

    e8b352ba526c940f266c22db29fc1b22.png

    电源模块设计

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

    ac2569391da223061cf0c1d439d79d86.png

    电机驱动模块设计

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

    37bcc99c6291d22566ebbf672dd69c83.png

    电源驱动引脚表

    控制芯片: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实物图如下:

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

    0856dc0eaab6bce46c2153355cd4027b.png

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

    /* 获取接收到的高电平的时间(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型号的舵机,舵机是一种常见的角度驱动器,本系统需要判断不同位置的障碍物可以且对转向的力度小。舵机可以理解为方向盘称,方向盘是一个常见的名字。它实际上是一个伺服马达。舵机实物图如下:

    743f9859020470c303e09074010da164.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%, 这就是具体的算法。

    舵机重要代码(可参考)

    /**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编码器的实物图如下:

    ef13f589608501c0064df5a53cecf9af.png

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

    4aef2de16fd3d4edc47bcfb82ce34ebb.png
    展开全文
  • 【项目分享】基于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超声波避障小车。这也是我曾经的一个课设,在此开源分享给大家,全文5000多字,干货满...
    不管是对于初学者还是对于一个玩过单片机的电子爱好者来说,或多或少都接触到过小车项目,今天给大家介绍的的一个项目 基于STM32超声波避障小车 这也是我曾经的一个课设,在此开源分享给大家,全文5000多字,干货满满,加油读完 9126123be93311355be7fb4a65c34469.png,保证你收货多多 65102a4bd98b71447c5f4e7a64df5a1e.png

    话不多说先来看视频332f7faa042fb7fc5086bc78d126ca73.png


    处理器电路设计


    单片机是系统的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就可以轻松完成。控制器模块电路如下所示:

    4714ac1c2c5426a4c2cd010e5ea09a32.png


    电源模块设计


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

    103f5a4d2d1cfd3a9c247b357f2a04d3.png


    电机驱动模块设计


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

    86c71db83f88f6e31fb379d72fc19134.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_1210     BO1        PWMB--------->TIM1_CH2(PA9)11     GND        GND12    2号TB6612引脚分配:13     VM         PWMA--------->TIM1_CH3(PA10)14     VCC        AIN2--------->GPIOB_515     GND        AIN1--------->GPIOB_616     AO1        STBY--------->GPIOB_717     AO2        BIN1--------->GPIOB_818     BO2        BIN2--------->GPIOA_919     BO1        PWMB--------->TIM1_CH4(PA11)20     GND        GND21真值表22     AIN1   0     1     0     123     AIN2   0     0     1     124     BIN1   0     1     0     125     BIN2   0     0     1     126           停止  正转  反转  刹车

    电机所用到的定时器配置

    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 通道139    TIM_OC2Init(TIMx,&TIM_OCInitStructure);//初始化TIM1 通道240    TIM_OC3Init(TIMx,&TIM_OCInitStructure);//初始化TIM1 通道341    TIM_OC4Init(TIMx,&TIM_OCInitStructure);//初始化TIM1 通道442    /*自动重装载*/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实物图如下:

    d7371ede8d7bc7930a19d87f4f9885dd.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。

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

    c6bbe3808ece5a48fd293bc28d204ac9.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;2223        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);3233        }break;34        default:break;35    }36    count++;37}


    舵机模块     


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

    1ead35a7eb92c1854fe9ffe58008d86b.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,PSC15//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---------0621.0ms---------45   631.5ms---------90642.0ms---------135652.5ms-----------180662.1ms    turn_left=150670.8ms    turn_right=25681.3ms    turn_front=756920ms的时基脉冲,如果想让舵机转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=72MHZ80               = 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编码器的实物图如下:

    c47b55036525145a436b29f40d94bfa7.png


    OLED显示模块


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

    END


    微信公众号:果果小师弟
    关注可了解获取更多的免费资料;如果你觉得文章对你有帮助,转发分享,欢迎赞赏

    最后

    10T资源大放送!包括但不限于:C/C++,Linux,Python,Java,PHP,人工智能,PCB、FPGA、DSP、单片机、等等!在公众号内回复「更多资源」,即可免费获取,期待你的关注!!!
    下面的是我的公众号二维码图片,欢迎关注。

    4603d224e7dfc940f1af2d9b8cd8335a.png

    展开全文
  • 资源包括:程序,程序设计报告 ...思路:通过驱动超声波模块,测量出小车与障碍物之间的距离,并通过蓝牙反馈数据到手机,同时根据测量数据进行舵机的转动控制,从而实现智能避障(建议先看程序设计报告总结部分)
  • 智能小车使用红外探头避障,适应不同颜色标志
  • 基于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);
    		}
    	}
     }
    
    展开全文
  • 基于STM32C8T6的超声波避障小车,功能描述:检测到前方有障碍物,后退一下,然后左转90度直行离开。另外里面还附赠循迹函数(写了但是没调用),循迹函数可以实现的功能:虚线转弯,直角弯,弧形弯,冲坡,十字路口...
  • 这是一款基于STM32F407开发板的蓝牙控制小车的keil程序。其中包含有步进电机的PWM控制,HC-05蓝牙模块控制小车前进后退转向,HC-SR04超声波避障、DHT-11温湿度传感器上传温湿度、光敏电阻等。
  • 基于STM32三路超声波避障小车

    万次阅读 多人点赞 2019-08-03 17:40:18
    一般学习单片机的第一步就是做智能小车,通过PWM控制调节车速,以及使用超声波模块进行输入捕获,而这篇文章在于多通道实现输入捕获,因为用到了3个超声波。详细讲解了输入捕获的原理以及一些重要功能的实现代码
  • stm32标准库两路超声波避障小车.rar
  • STM32循迹避障小车制作代码详解(简单实现版)

    万次阅读 多人点赞 2018-11-16 11:29:05
    写在最前:最近由于需要制作了一个循迹避障小车,制作比较简单但是还是出现了很多bug,因此在博客中记录一下,希望对后期需要制作的能有所帮助,小车由PWM信号+L293D驱动。(二轮驱动,第三轮为自由轮) 要求:小车...
  • 本代码基于STM32F103ZET6单片机,使用一个超声波传感器,四个万向轮,四轮驱动,实现小车自主换道避障
  • stm32 ,多路超声波避障小车,L298N驱动,PWM调速,LCD屏显示。程序注释一应俱全,让你学会规范的代码写法。
  • 基于STM32的智能车,利用超声波和循迹模块实现避障以及超声波检测的功能。
  • stm32-超声波避障程序案例,循迹+壁障+遥控功能,蓝牙遥控
  • 设计基于STM32F103ZET6主控芯片的智能避障小车,由电源模块、障碍物检测模块、电机驱动模块、红外遥控模块部分组成,其中HC-SR04超声波传感器、HJ-IR2红外传感器、MMA7361L倾角传感器协同组成障碍物检测部分完成障碍物...
  • 本设计主要有三个模块,包括信号检测模块、主控模块、电机驱动模块。信号检测模块采用灰度传感器和超声波,用以对有无障碍与跟随黑线进行检测。...通过有无光线接收来控制电动小车的转向,从而实现自动循迹避障的功能。
  • stm32 避障小车

    2019-02-14 17:33:22
    使用stm32实现的智能避障小车,代码详细,欢迎大家下载
  • stm32 智能避障小车(一)

    万次阅读 多人点赞 2019-02-12 11:40:52
    避障小车制作过程 今年寒假期间,我再csdn上看到别人制作的避障小车,我想了想,越想越觉得好玩,于是在这20多天里,我基本已经做到我想要的效果了,这期间我也是很烦恼,很简单的制作,我花费了这么长的时间,我...
  • 基于stm32单片机的红外遥控超声波避障小车

    千次阅读 多人点赞 2020-01-29 00:50:08
    基于stm32单片机的红外遥控超声波避障小车简述小车成果展示驱动如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表...
  • 基于stm32-小车超声波避障,库版本v3.5

空空如也

空空如也

1 2 3 4 5 ... 18
收藏数 347
精华内容 138
关键字:

stm32超声波避障小车