你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【项目分享】基于STM32超声波避障小车

[复制链接]
STMCU 发布时间:2020-10-9 10:44
【项目分享】基于STM32超声波避障小车


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



处理器电路设计
单片机是系统的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就可以轻松完成。控制器模块电路如下所示:
2.png
电源模块设计
本设计采用锂电池供电, 模块的供电电压一般都为5V,同时超声波模块需要较大的电流才能正常工作,所以在降压的基础上也要保证足够大的输出电流。本设计采用可调输出版本,模块的输入电压范围广,输出电压在1.25V-35V内可调,电压转换效率高,输出纹波小。降压电路如下所示:
3.png
电机驱动模块设计
要完成转向是能够利用单片机实现的,然而单片机I0的带负载能力弱,因此我们选择了大功率放大器件TB6612FNG。TB6612FNG是采用MOSFET-H桥结构的双通道大电流电路输出,可以控制2个电机的驱动。相比普通的电机驱动,外围电路非常简单,只需要一个芯片和一个钽电容进行PWM输出滤波,系统尺寸小。PWM信号输入频率范围广,轻松满足本设计的需求。   
4.png
电机驱动引脚表
  1. 1控制芯片:TB6612
  2. 2控制芯片数量:2
  3. 3    1号TB6612引脚分配:
  4. 4     VM         PWMA--------->TIM1_CH1(PA8)
  5. 5     VCC        AIN2--------->GPIOB_12
  6. 6     GND        AIN1--------->GPIOB_13
  7. 7     AO1        STBY--------->GPIOB_14
  8. 8     AO2        BIN1--------->GPIOB_15
  9. 9     BO2        BIN2--------->GPIOA_12
  10. 10     BO1        PWMB--------->TIM1_CH2(PA9)
  11. 11     GND        GND
  12. 12    2号TB6612引脚分配:
  13. 13     VM         PWMA--------->TIM1_CH3(PA10)
  14. 14     VCC        AIN2--------->GPIOB_5
  15. 15     GND        AIN1--------->GPIOB_6
  16. 16     AO1        STBY--------->GPIOB_7
  17. 17     AO2        BIN1--------->GPIOB_8
  18. 18     BO2        BIN2--------->GPIOA_9
  19. 19     BO1        PWMB--------->TIM1_CH4(PA11)
  20. 20     GND        GND
  21. 21真值表
  22. 22     AIN1   0     1     0     1
  23. 23     AIN2   0     0     1     1
  24. 24     BIN1   0     1     0     1
  25. 25     BIN2   0     0     1     1
  26. 26           停止  正转  反转  刹车
复制代码


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



超声波模块


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

注意:在硬件操作上需要首先让模块地端先连接,否则会影响模块工作。测距时,被测物体的摆放不能太过于杂乱,否则会影响测试结果。
6.png
超声波重要代码(可参考)
  1. 1/* 获取接收到的高电平的时间(us*/
  2. 2uint32_t Get_HC_SR04_Time(void)
  3. 3{
  4. 4    uint32_t t=0;
  5. 5    t=Acoustic_Distance_Count*1000;//us
  6. 6    t+=TIM_GetCounter(TIM2);//获取us
  7. 7    TIM2->CNT =0;
  8. 8    Acoustic_Distance_Count=0;
  9. 9    Systic_Delay_us(100);
  10. 10    return t;
  11. 11}
  12. 12/*获取距离*/
  13. 13void Get_HC_SR04_Distance(void)
  14. 14{
  15. 15    static uint16_t count=0;
  16. 16    switch(count)
  17. 17    {
  18. 18        case 1:
  19. 19        {
  20. 20            GPIO_SetBits(Acoustic_Port,HC_SR04_Trig);//Trig发送一个大于10us的高电平
  21. 21        }break;
  22. 22
  23. 23        case 15:
  24. 24        {
  25. 25            count=0;
  26. 26            GPIO_ResetBits(Acoustic_Port,HC_SR04_Trig);
  27. 27            while(GPIO_ReadInputDataBit(Acoustic_Port,HC_SR04_Echo)==0);//当Echo为0时打开定时器 计时
  28. 28            Open_Tim2();
  29. 29            while(GPIO_ReadInputDataBit(Acoustic_Port,HC_SR04_Echo)==1);//当Echo为0时打开定时器 计时
  30. 30            Close_Tim2();
  31. 31            HC_SR04_Distance=(float)(Get_HC_SR04_Time()/5.78);
  32. 32
  33. 33        }break;
  34. 34        default:break;
  35. 35    }
  36. 36    count++;
  37. 37}
复制代码





舵机模块
   

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


编码器模块

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

OLED显示模块

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




文章出处:程序员小哈
收藏 4 评论1 发布时间:2020-10-9 10:44

举报

1个回答
hujjj 回答时间:2020-10-9 12:15:04
不错的资料,期待后续。

所属标签

STM32团队

意法半导体微控制器和微处理器拥有广泛的产品线,包含低成本的8位单片机和基于ARM® Cortex®-M0、M0+、M3、M4、M33、M7及A7内核并具备丰富外设选择的32位微控制器及微处理器


最新内容

相似技术帖

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
微信公众号二维码 微信公众号
手机版二维码 手机版