当前位置:   article > 正文

STM32蓝牙小车、红外循迹小车、超声波避障小车项目设计_红外及超声波避障的单片机程序

红外及超声波避障的单片机程序

一、前言

本文旨在分享我学习STM32的过程中,为了强化学习成果,试着制作一些实训项目。最开始做的就是STM32蓝牙小车、STM32红外循迹小车、STM32超声波避障小车。

相信看完本文的你,一定可以亲手制作一辆属于自己的智能小车!

注:文末附源码工程,需要的读者可以至文末下载

如果你还想进阶的话,可以尝试制作基于PID算法的两轮平衡小车——这是它的相关源码工程。STM32两轮平衡小车原理详解(开源)_stm32平衡车原理-CSDN博客


二、实训项目 

(一)、项目概述

1、共同需要的驱动模块

关于以上三种功能的智能小车,都需要用到一些共同的驱动模块,这里我先把他们共同用到的模块罗列出来。

A、模块一:TB6612电机驱动模块

关于该模块的使用方式,请看以下文章:

B、模块二:两轮或四轮车模

为了满足大家的需求,我会在讲解电机驱动时分别讲两种电机:

(1)一种时大家看到的下图的直流电机

(2)另一种是大家熟悉的编码电机

C、模块三:电源 

 D、模块四:电源稳压模块(降压模块)

E、模块四:stm32f103c8t6最小系统板


2、共同的驱动代码

为什么我这里要先讲述以上三个功能的共同驱动代码呢?因为以上三个功能都是基于同一个功能下实现的,这个功能就是——

首先车必须能动!!!

那么车动的驱动代码是如何实现的呢?不了解TB6612电机驱动模块的小伙伴建议先去了解一下该模块的使用的方法!

 这里先讲一下直流电机的驱动:

 (1)直流电机
单片机驱动引脚配置:
  1. void CarGo(void)
  2. {
  3. TIM_SetCompare4(TIM1 , 170);
  4. TIM_SetCompare1(TIM1 , 170);
  5. GPIO_SetBits(GPIOB,GPIO_Pin_13); //控制左边轮胎
  6. GPIO_ResetBits(GPIOB,GPIO_Pin_12);
  7. GPIO_ResetBits(GPIOB,GPIO_Pin_1); //控制右边轮胎
  8. GPIO_SetBits(GPIOB,GPIO_Pin_0);
  9. }
PWM配置: 
  1. #include "PWM1.h" // Device header
  2. void PWM1_Init(void)
  3. {
  4. GPIO_InitTypeDef GPIO_InitStructure;
  5. RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
  6. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
  7. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  8. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8|GPIO_Pin_11;
  9. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  10. GPIO_Init(GPIOA, &GPIO_InitStructure);
  11. TIM_InternalClockConfig(TIM1);
  12. TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
  13. TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
  14. TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
  15. TIM_TimeBaseInitStructure.TIM_Period = 1000 - 1; //ARR
  16. TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1; //PSC
  17. TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
  18. TIM_TimeBaseInit(TIM1, &TIM_TimeBaseInitStructure);
  19. TIM_OCInitTypeDef TIM_OCInitStructure;
  20. TIM_OCStructInit(&TIM_OCInitStructure);
  21. TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
  22. TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
  23. TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
  24. TIM_OCInitStructure.TIM_Pulse = 0; //CCR
  25. TIM_OC1Init(TIM1, &TIM_OCInitStructure);
  26. TIM_OC4Init(TIM1, &TIM_OCInitStructure);
  27. TIM_Cmd(TIM1, ENABLE);
  28. TIM_CtrlPWMOutputs(TIM1,ENABLE);
  29. }
  30. //PWM输出初始化
  31. //arr:自动重装值
  32. //psc:时钟预分频数
  33. //TIM1_PWM_Init(7199,0);//PWM频率=72000/(7199+1)=10Khz
  34. //void TIM1_PWM_Init(u16 arr,u16 psc)
  35. //{
  36. // GPIO_InitTypeDef GPIO_InitStructure;
  37. // TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
  38. // TIM_OCInitTypeDef TIM_OCInitStructure;
  39. // RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);//
  40. // RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE); //使能GPIO外设时钟使能
  41. // //设置该引脚为复用输出功能,输出TIM1 CH1 CH4的PWM脉冲波形
  42. // GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8|GPIO_Pin_11; //TIM_CH1 //TIM_CH4
  43. // GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
  44. // GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  45. // GPIO_Init(GPIOA, &GPIO_InitStructure);
  46. //
  47. // TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
  48. // TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值 不分频
  49. // TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
  50. // TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
  51. // TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
  52. //
  53. // TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式1
  54. // TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
  55. // TIM_OCInitStructure.TIM_Pulse = 0; //设置待装入捕获比较寄存器的脉冲值
  56. // TIM_OCInitStructure.TIM_Pulse = arr >> 1;
  57. // TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
  58. // TIM_OC1Init(TIM1, &TIM_OCInitStructure); //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
  59. // TIM_OC4Init(TIM1, &TIM_OCInitStructure); //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
  60. // TIM_CtrlPWMOutputs(TIM1,ENABLE); //MOE 主输出使能
  61. // TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable); //CH1预装载使能
  62. // TIM_OC4PreloadConfig(TIM1, TIM_OCPreload_Enable); //CH4预装载使能
  63. //
  64. // TIM_ARRPreloadConfig(TIM1, ENABLE); //使能TIMx在ARR上的预装载寄存器
  65. //
  66. // TIM_Cmd(TIM1, ENABLE); //使能TIM1
  67. //}
  1. #ifndef __PWM1_H
  2. #define __PWM1_H
  3. #include "sys.h"
  4. #include "stm32f10x_tim.h"
  5. void TIM1_PWM_Init(u16 arr,u16 psc);
  6. #endif
 直流电机驱动:
  1. #include "stm32f10x.h" // Device header
  2. #include "PWM1.h"
  3. #include "motor.h"
  4. /*
  5. 一个端口接PWM,还有一个接高低电平,无论接哪个电平输出的PWM波形不变,若PWM输出30
  6. IO接负:PWM的上半段输出电压,则给电机30的电压,正转
  7. IO接正;PWM的上半段输出电压,则给电机70的电压,负转
  8. */
  9. void CarGo(void)
  10. {
  11. TIM_SetCompare4(TIM1 , 170);
  12. TIM_SetCompare1(TIM1 , 170);
  13. GPIO_SetBits(GPIOB,GPIO_Pin_13); //控制左边轮胎
  14. GPIO_ResetBits(GPIOB,GPIO_Pin_12);
  15. GPIO_ResetBits(GPIOB,GPIO_Pin_1); //控制右边轮胎
  16. GPIO_SetBits(GPIOB,GPIO_Pin_0);
  17. }
  18. void CarStop(void)
  19. {
  20. TIM_SetCompare4(TIM1 , 0);
  21. TIM_SetCompare1(TIM1 , 0);
  22. GPIO_SetBits(GPIOB,GPIO_Pin_13); //控制左边轮胎
  23. GPIO_SetBits(GPIOB,GPIO_Pin_12);
  24. GPIO_SetBits(GPIOB,GPIO_Pin_1); //控制右边轮胎
  25. GPIO_SetBits(GPIOB,GPIO_Pin_0);
  26. }
  27. void CarLeft(void)
  28. {
  29. TIM_SetCompare4(TIM1 , 100);
  30. TIM_SetCompare1(TIM1 , 200);
  31. GPIO_SetBits(GPIOB,GPIO_Pin_13); //控制左边轮胎
  32. GPIO_ResetBits(GPIOB,GPIO_Pin_12);
  33. GPIO_ResetBits(GPIOB,GPIO_Pin_1); //控制右边轮胎
  34. GPIO_SetBits(GPIOB,GPIO_Pin_0);
  35. }
  36. void CarBigLeft(void)
  37. {
  38. TIM_SetCompare4(TIM1 , 100);
  39. TIM_SetCompare1(TIM1 , 250);
  40. GPIO_SetBits(GPIOB,GPIO_Pin_13); //控制左边轮胎
  41. GPIO_ResetBits(GPIOB,GPIO_Pin_12);
  42. GPIO_ResetBits(GPIOB,GPIO_Pin_1); //控制右边轮胎
  43. GPIO_SetBits(GPIOB,GPIO_Pin_0);
  44. }
  45. void CarLeftAround(void)
  46. {
  47. TIM_SetCompare4(TIM1 , 0);
  48. TIM_SetCompare1(TIM1 , 0);
  49. GPIO_SetBits(GPIOB,GPIO_Pin_13); //控制左边轮胎
  50. GPIO_ResetBits(GPIOB,GPIO_Pin_12);
  51. GPIO_ResetBits(GPIOB,GPIO_Pin_1); //控制右边轮胎
  52. GPIO_SetBits(GPIOB,GPIO_Pin_0);
  53. }
  54. void CarRight(void)
  55. {
  56. TIM_SetCompare4(TIM1 , 200);
  57. TIM_SetCompare1(TIM1 , 100);
  58. GPIO_SetBits(GPIOB,GPIO_Pin_13); //控制左边轮胎
  59. GPIO_ResetBits(GPIOB,GPIO_Pin_12);
  60. GPIO_ResetBits(GPIOB,GPIO_Pin_1); //控制右边轮胎
  61. GPIO_SetBits(GPIOB,GPIO_Pin_0);
  62. }
  63. void CarBigRight(void)
  64. {
  65. TIM_SetCompare4(TIM1 , 250);
  66. TIM_SetCompare1(TIM1 , 100);
  67. GPIO_SetBits(GPIOB,GPIO_Pin_13); //控制左边轮胎
  68. GPIO_ResetBits(GPIOB,GPIO_Pin_12);
  69. GPIO_ResetBits(GPIOB,GPIO_Pin_1); //控制右边轮胎
  70. GPIO_SetBits(GPIOB,GPIO_Pin_0);
  71. }
  72. void CarRightAround(void)
  73. {
  74. TIM_SetCompare4(TIM1 , 400);
  75. TIM_SetCompare1(TIM1 , 400);
  76. GPIO_SetBits(GPIOB,GPIO_Pin_13); //控制左边轮胎
  77. GPIO_ResetBits(GPIOB,GPIO_Pin_12);
  78. GPIO_ResetBits(GPIOB,GPIO_Pin_1); //控制右边轮胎
  79. GPIO_SetBits(GPIOB,GPIO_Pin_0);
  80. }
  81. void CarBack(void)
  82. {
  83. TIM_SetCompare4(TIM1 , 100);
  84. TIM_SetCompare1(TIM1 , 100);
  85. GPIO_ResetBits(GPIOB,GPIO_Pin_13); //控制右边轮胎
  86. GPIO_SetBits(GPIOB,GPIO_Pin_12);
  87. GPIO_SetBits(GPIOB,GPIO_Pin_1); //控制左边轮胎
  88. GPIO_ResetBits(GPIOB,GPIO_Pin_0);
  89. }
  90. void GPIO_init_Init(void)
  91. {
  92. GPIO_InitTypeDef GPIO_InitStructure;
  93. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//使能GPIO的外设时钟
  94. GPIO_InitStructure.GPIO_Pin =GPIO_Pin_13|GPIO_Pin_12|GPIO_Pin_1|GPIO_Pin_0;//选择要用的GPIO引脚
  95. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //设置引脚模式为推挽输出模式
  96. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//设置引脚速度为50MHZ
  97. GPIO_Init(GPIOB,&GPIO_InitStructure);//调用库函数,初始化GPIO
  98. }
  99. /*-------------------
  100. 函数功能:
  101. 提供电机PWM接口函数,将PID计算好的PWM直接赋值给电机
  102. --------------------*/
  103. void ZhengZhuan()
  104. {
  105. GPIO_SetBits(GPIOB,GPIO_Pin_13); //控制右边轮胎
  106. GPIO_ResetBits(GPIOB,GPIO_Pin_12);
  107. GPIO_SetBits(GPIOB,GPIO_Pin_1); //控制左边轮胎
  108. GPIO_ResetBits(GPIOB,GPIO_Pin_0);
  109. }
  110. void FanZhuan()
  111. {
  112. GPIO_ResetBits(GPIOB,GPIO_Pin_12); //控制右边轮胎
  113. GPIO_SetBits(GPIOB,GPIO_Pin_13);
  114. GPIO_ResetBits(GPIOB,GPIO_Pin_0); //控制左边轮胎
  115. GPIO_SetBits(GPIOB,GPIO_Pin_1);
  116. }
  117. void Stop()
  118. {
  119. GPIO_SetBits(GPIOB,GPIO_Pin_13); //控制右边轮胎
  120. GPIO_SetBits(GPIOB,GPIO_Pin_12);
  121. GPIO_SetBits(GPIOB,GPIO_Pin_1); //控制左边轮胎
  122. GPIO_SetBits(GPIOB,GPIO_Pin_0);
  123. }
  124. void motor_pwm(float duty) //0 -- 7200
  125. {
  126. if(duty>=0 && duty <= 5000 )
  127. {
  128. ZhengZhuan();//====电机正转,逆时针
  129. TIM_SetCompare1(TIM1, duty);
  130. TIM_SetCompare4(TIM1, duty);
  131. }
  132. else if(duty > 5000)
  133. {
  134. Stop();
  135. }
  136. else
  137. {
  138. FanZhuan();
  139. TIM_SetCompare2(TIM1, duty);
  140. TIM_SetCompare1(TIM1, duty);
  141. }
  142. }

(2)、编码电机

编码电机实质和直流电机一样,只不过其本身自带一个编码测速盘,所以我在这里详细讲述一下编码器的使用方式。

Encoder.c
  1. #include "stm32f10x.h" // Device header
  2. #include "Encoder.h"
  3. #include "sys.h"
  4. //电机编码器线数大约位263
  5. void Encoder2_Init()
  6. {
  7. TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
  8. TIM_ICInitTypeDef TIM_ICInitStructure;
  9. GPIO_InitTypeDef GPIO_InitStructure;
  10. RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);//使能定时器4的时钟
  11. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);//使能PB端口时钟
  12. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7; //端口配置
  13. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
  14. GPIO_Init(GPIOB, &GPIO_InitStructure); //根据设定参数初始化GPIOB
  15. TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
  16. TIM_TimeBaseStructure.TIM_Prescaler = 0x0; // 预分频器
  17. TIM_TimeBaseStructure.TIM_Period = ENCODER_TIM_PERIOD; //设定计数器自动重装值
  18. TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;//选择时钟分频:不分频
  19. TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;TIM向上计数
  20. TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);
  21. TIM_EncoderInterfaceConfig(TIM4, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);//使用编码器模式3
  22. TIM_ICStructInit(&TIM_ICInitStructure);
  23. TIM_ICInitStructure.TIM_ICFilter = 10;
  24. TIM_ICInit(TIM4, &TIM_ICInitStructure);
  25. TIM_ClearFlag(TIM4, TIM_FLAG_Update);//清除TIM的更新标志位
  26. TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE);
  27. //Reset counter
  28. TIM_SetCounter(TIM4,0);
  29. TIM_Cmd(TIM4, ENABLE);
  30. }
  31. void Encoder1_Init()
  32. {
  33. GPIO_InitTypeDef GPIO_InitStructure;
  34. //开启时钟
  35. RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
  36. //输入比较用的TIM2,所以输入捕获用TIM3,不能用一个,TIM3也在APB1
  37. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
  38. //配置GPIO
  39. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//上拉输入
  40. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; //TIM3的通道一的引脚在PA6
  41. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  42. GPIO_Init(GPIOA, &GPIO_InitStructure);
  43. //配置时基单元
  44. TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
  45. TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
  46. TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
  47. TIM_TimeBaseInitStructure.TIM_Period = 65536 - 1; //ARR,设大,防止溢出
  48. TIM_TimeBaseInitStructure.TIM_Prescaler = 1 - 1; //PSC
  49. TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
  50. TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);
  51. TIM_ICInitTypeDef TIM_ICInitStructure;
  52. TIM_ICStructInit(&TIM_ICInitStructure); //给默认的初始值
  53. TIM_ICInitStructure.TIM_Channel = TIM_Channel_1; //通道一
  54. TIM_ICInitStructure.TIM_ICFilter = 0xF; //滤波大小
  55. TIM_ICInit(TIM3, &TIM_ICInitStructure);
  56. TIM_ICInitStructure.TIM_Channel = TIM_Channel_2; //通道一
  57. TIM_ICInitStructure.TIM_ICFilter = 0xF; //滤波大小
  58. TIM_ICInit(TIM3, &TIM_ICInitStructure);
  59. TIM_EncoderInterfaceConfig(TIM3,TIM_EncoderMode_TI12,TIM_ICPolarity_Rising,TIM_ICPolarity_Rising);
  60. //配置编码器接口
  61. TIM_Cmd(TIM3,ENABLE);
  62. }
  63. int Read_Encoder(u8 TIMX)
  64. {
  65. int Encoder_TIM;
  66. switch(TIMX)
  67. {
  68. case 4:
  69. Encoder_TIM= (short)TIM4 -> CNT;
  70. TIM4 -> CNT=0;
  71. break;
  72. case 3:
  73. Encoder_TIM= (short)TIM3 -> CNT; TIM3 -> CNT=0;
  74. break;
  75. default: Encoder_TIM=0;
  76. }
  77. return Encoder_TIM;
  78. }
 Encoder.h
  1. #ifndef __ENCODER_H
  2. #define __ENCODER_H
  3. #define ENCODER_TIM_PERIOD (u16)(65535) //103的定时器是16位 2的16次方最大是65536
  4. //uint16_t Encoder1_get(void);
  5. void Encoder1_Init(void);
  6. void Encoder2_Init(void);
  7. int Read_Encoder(u8 TIMX);
  8. #endif

 提示:编码电机的驱动和直流电机一样,所以不再赘述。

(二)、实训项目——蓝牙小车

Step1:器材准备

在以上共同的器材上添加一个蓝牙模块

 Step2:核心代码分析

设计蓝牙小车,由于蓝牙是串口通信,所以关键就是如何配置串口,保证数据收发正常。

对于单片机的串口配置,请详细看下面的代码: 

Usart.c
  1. #include "sys.h"
  2. #include "usart1.h"
  3. //
  4. //如果使用ucos,则包括下面的头文件即可.
  5. #if SYSTEM_SUPPORT_OS
  6. #include "includes.h" //ucos 使用
  7. #endif
  8. //
  9. //本程序只供学习使用,未经作者许可,不得用于其它任何用途
  10. //ALIENTEK STM32开发板
  11. //串口1初始化
  12. //正点原子@ALIENTEK
  13. //技术论坛:www.openedv.com
  14. //修改日期:2012/8/18
  15. //版本:V1.5
  16. //版权所有,盗版必究。
  17. //Copyright(C) 广州市星翼电子科技有限公司 2009-2019
  18. //All rights reserved
  19. //********************************************************************************
  20. //V1.3修改说明
  21. //支持适应不同频率下的串口波特率设置.
  22. //加入了对printf的支持
  23. //增加了串口接收命令功能.
  24. //修正了printf第一个字符丢失的bug
  25. //V1.4修改说明
  26. //1,修改串口初始化IO的bug
  27. //2,修改了USART_RX_STA,使得串口最大接收字节数为214次方
  28. //3,增加了USART_REC_LEN,用于定义串口最大允许接收的字节数(不大于214次方)
  29. //4,修改了EN_USART1_RX的使能方式
  30. //V1.5修改说明
  31. //1,增加了对UCOSII的支持
  32. //
  33. //
  34. //加入以下代码,支持printf函数,而不需要选择use MicroLIB
  35. #if 1
  36. #pragma import(__use_no_semihosting)
  37. //标准库需要的支持函数
  38. struct __FILE
  39. {
  40. int handle;
  41. };
  42. FILE __stdout;
  43. //定义_sys_exit()以避免使用半主机模式
  44. void _sys_exit(int x)
  45. {
  46. x = x;
  47. }
  48. //重定义fputc函数
  49. int fputc(int ch, FILE *f)
  50. {
  51. while((USART1->SR&0X40)==0);//循环发送,直到发送完毕
  52. USART1->DR = (u8) ch;
  53. return ch;
  54. }
  55. #endif
  56. #if EN_USART1_RX //如果使能了接收
  57. //串口1中断服务程序
  58. //注意,读取USARTx->SR能避免莫名其妙的错误
  59. u8 USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.
  60. //接收状态
  61. //bit15, 接收完成标志
  62. //bit14, 接收到0x0d
  63. //bit13~0, 接收到的有效字节数目
  64. u16 USART_RX_STA=0; //接收状态标记
  65. void uart1_init(u32 bound){
  66. //GPIO端口设置
  67. GPIO_InitTypeDef GPIO_InitStructure;
  68. USART_InitTypeDef USART_InitStructure;
  69. NVIC_InitTypeDef NVIC_InitStructure;
  70. RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); //使能USART1,GPIOA时钟
  71. //USART1_TX GPIOA.9
  72. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
  73. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  74. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
  75. GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9
  76. //USART1_RX GPIOA.10初始化
  77. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
  78. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
  79. GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10
  80. //Usart1 NVIC 配置
  81. NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
  82. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
  83. NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3
  84. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
  85. NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
  86. //USART 初始化设置
  87. USART_InitStructure.USART_BaudRate = bound;//串口波特率
  88. USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
  89. USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
  90. USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
  91. USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
  92. USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式
  93. USART_Init(USART1, &USART_InitStructure); //初始化串口1
  94. USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断
  95. USART_Cmd(USART1, ENABLE); //使能串口1
  96. }
  97. void USART1_IRQHandler(void) //串口1中断服务程序
  98. {
  99. u8 Res;
  100. #if SYSTEM_SUPPORT_OS //如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
  101. OSIntEnter();
  102. #endif
  103. if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断(接收到的数据必须是0x0d 0x0a结尾)
  104. {
  105. Res =USART_ReceiveData(USART1); //读取接收到的数据
  106. if((USART_RX_STA&0x8000)==0)//接收未完成
  107. {
  108. if(USART_RX_STA&0x4000)//接收到了0x0d
  109. {
  110. if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始
  111. else USART_RX_STA|=0x8000; //接收完成了
  112. }
  113. else //还没收到0X0D
  114. {
  115. if(Res==0x0d)USART_RX_STA|=0x4000;
  116. else
  117. {
  118. USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
  119. USART_RX_STA++;
  120. if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收
  121. }
  122. }
  123. }
  124. }
  125. #if SYSTEM_SUPPORT_OS //如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
  126. OSIntExit();
  127. #endif
  128. }
  129. #endif
Usart.h
  1. #ifndef __USART1_H
  2. #define __USART1_H
  3. #include "stdio.h"
  4. #include "sys.h"
  5. //
  6. //本程序只供学习使用,未经作者许可,不得用于其它任何用途
  7. //ALIENTEK STM32开发板
  8. //串口1初始化
  9. //正点原子@ALIENTEK
  10. //技术论坛:www.openedv.com
  11. //修改日期:2012/8/18
  12. //版本:V1.5
  13. //版权所有,盗版必究。
  14. //Copyright(C) 广州市星翼电子科技有限公司 2009-2019
  15. //All rights reserved
  16. //********************************************************************************
  17. //V1.3修改说明
  18. //支持适应不同频率下的串口波特率设置.
  19. //加入了对printf的支持
  20. //增加了串口接收命令功能.
  21. //修正了printf第一个字符丢失的bug
  22. //V1.4修改说明
  23. //1,修改串口初始化IO的bug
  24. //2,修改了USART_RX_STA,使得串口最大接收字节数为214次方
  25. //3,增加了USART_REC_LEN,用于定义串口最大允许接收的字节数(不大于214次方)
  26. //4,修改了EN_USART1_RX的使能方式
  27. //V1.5修改说明
  28. //1,增加了对UCOSII的支持
  29. #define USART_REC_LEN 200 //定义最大接收字节数 200
  30. #define EN_USART1_RX 1 //使能(1/禁止(0)串口1接收
  31. //#define EN_USART2_RX 1 //使能(1/禁止(0)串口1接收
  32. extern u8 USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符
  33. extern u16 USART_RX_STA; //接收状态标记
  34. //如果想串口中断接收,请不要注释以下宏定义
  35. void uart1_init(u32 bound);
  36. //void uart_init2(u32 bound);
  37. #endif

(三)、实训项目——超声波避障小车

超声波避障,关键就是如何驱动超声波模块,以达到测距避障功能

超声波驱动核心代码

这里需要注意一个问题就是,超声波测距应该放在中断里实现!!!!

CS.c

  1. #include "cs.h"
  2. #include "stm32f10x.h"
  3. #include "delay.h"
  4. #include "usart.h"
  5. /*记录定时器溢出次数*/
  6. uint overcount=0;
  7. /*设置中断优先级*/
  8. void NVIC_Config(void)
  9. {
  10. NVIC_InitTypeDef NVIC_InitStructer;
  11. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
  12. NVIC_InitStructer.NVIC_IRQChannelPreemptionPriority=0;
  13. NVIC_InitStructer.NVIC_IRQChannelSubPriority=0;
  14. NVIC_InitStructer.NVIC_IRQChannel=TIM4_IRQn;
  15. NVIC_InitStructer.NVIC_IRQChannelCmd=ENABLE;
  16. NVIC_Init(&NVIC_InitStructer);
  17. }
  18. /*初始化模块的GPIO以及初始化定时器TIM2*/
  19. void CH_SR04_Init(void)
  20. {
  21. GPIO_InitTypeDef GPIO_InitStructer;
  22. TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructer;
  23. RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE);
  24. RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
  25. /*TRIG触发信号*/
  26. GPIO_InitStructer.GPIO_Speed=GPIO_Speed_50MHz; //原来接PB 8、PB 9不能用,后改为PA 11,PA 12就能用
  27. GPIO_InitStructer.GPIO_Mode=GPIO_Mode_Out_PP;
  28. GPIO_InitStructer.GPIO_Pin=GPIO_Pin_11;
  29. GPIO_Init(GPIOA, &GPIO_InitStructer);
  30. /*ECHO回响信号*/
  31. GPIO_InitStructer.GPIO_Mode=GPIO_Mode_IN_FLOATING;
  32. GPIO_InitStructer.GPIO_Pin=GPIO_Pin_12;
  33. GPIO_Init(GPIOA, & GPIO_InitStructer);
  34. GPIO_ResetBits(GPIOA,GPIO_Pin_4);
  35. /*定时器TIM2初始化*/
  36. TIM_DeInit(TIM4);
  37. TIM_TimeBaseInitStructer.TIM_Period=999;//定时周期为1000
  38. TIM_TimeBaseInitStructer.TIM_Prescaler=71; //分频系数72
  39. TIM_TimeBaseInitStructer.TIM_ClockDivision=TIM_CKD_DIV1;//不分频
  40. TIM_TimeBaseInitStructer.TIM_CounterMode=TIM_CounterMode_Up;
  41. TIM_TimeBaseInit(TIM4,&TIM_TimeBaseInitStructer);
  42. TIM_ClearFlag(TIM4,TIM_FLAG_Update);
  43. TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE);//开启更新中断
  44. NVIC_Config();
  45. TIM_Cmd(TIM4,DISABLE);//关闭定时器使能
  46. }
  47. float Senor_Using(void) //测距函数
  48. {
  49. float length=0,sum=0;
  50. u16 tim;
  51. uint i=0;
  52. /*5次数据计算一次平均值*/
  53. while(i!=5)
  54. {
  55. PAout(11)=1; //拉高信号,作为触发信号
  56. delay_us(20); //高电平信号超过10us
  57. PAout(11)=0;
  58. /*等待回响信号*/
  59. while(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_12)==RESET);
  60. TIM_Cmd(TIM4,ENABLE);//回响信号到来,开启定时器计数
  61. i+=1; //每收到一次回响信号+1,收到5次就计算均值
  62. while(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_12)==SET);//回响信号消失
  63. TIM_Cmd(TIM4,DISABLE);//关闭定时器
  64. tim=TIM_GetCounter(TIM4);//获取计TIM4数寄存器中的计数值,一边计算回响信号时间
  65. length=(tim+overcount*1000)/58.0;//通过回响信号计算距离
  66. sum=length+sum;
  67. TIM4->CNT=0; //将TIM4计数寄存器的计数值清零
  68. overcount=0; //中断溢出次数清零
  69. delay_ms(10);
  70. }
  71. length=sum/5;
  72. return length;//距离作为函数返回值
  73. }
  74. void TIM4_IRQHandler(void) //中断,当回响信号很长是,计数值溢出后重复计数,用中断来保存溢出次数
  75. {
  76. if(TIM_GetITStatus(TIM4,TIM_IT_Update)!=RESET)
  77. {
  78. TIM_ClearITPendingBit(TIM4,TIM_IT_Update);//清除中断标志
  79. overcount++;
  80. }
  81. }

Cs.h

  1. #ifndef __CS_H
  2. #define __CS_H
  3. #include "stm32f10x.h"
  4. #include "delay.h"
  5. #include "sys.h"
  6. #define uint unsigned int
  7. #define TRIG_Send PBout(8)
  8. #define ECHO_Reci PBin(9)
  9. void CH_SR04_Init(void); //超声波模块相关配置初始化
  10. float Senor_Using(void); //测距函数,返回值即为距离
  11. void NVIC_Config(void); //中断配置
  12. #endif

通过以上驱动代码,就能实现超声波测距了。

 (四)、实训项目——红外循迹

红外循迹相对简单,只需明白红外传感器在检测到黑色物质时和未检测到黑色物质时,传感器信号口传回的电平是不同的。通过单片机端口来判断传回的高低电平,不同位置的传感器传回的信号不同,从而可以根据不同位置传回的高低电平来控制车的运动。

定义几个单片机检测端口来判断红外 传感器传回的电平信号即可。

  1. #include "xunji.h"
  2. void xunji_config(void)
  3. {
  4. GPIO_InitTypeDef GPIO_InitStructure;
  5. RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE); // 使能PB端口时钟
  6. //配置电机引脚,不知道什么原因要在这里配置成上拉才能实现pwm调速
  7. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7|GPIO_Pin_14|GPIO_Pin_15; //选择对应的引脚
  8. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//配置GPIO模式,输入上拉
  9. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  10. GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化PB端口
  11. RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE); // 使能PA端口时钟
  12. //配置红外引脚
  13. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4| GPIO_Pin_5;; //选择对应的引脚
  14. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//配置GPIO模式,浮空输入
  15. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  16. GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化PC端口
  17. }
  18. //读取红外的信号
  19. void Read_xunji_Date(void)
  20. {
  21. L1;
  22. L2;
  23. M;
  24. R2;
  25. R1;
  26. }
  1. #ifndef __XUNJI_H
  2. #define __XUNJI_H
  3. #include "stm32f10x.h"
  4. #define L1 GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_1)
  5. #define L2 GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_2)
  6. #define M GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_3)
  7. #define R2 GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_4)
  8. #define R1 GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_5) // L1 L2 M R2 R1
  9. void xunji_config(void);
  10. void Read_xunji_Date(void); //读循迹模块返回的值
  11. #endif

三、总结

以上就是STM32红外循迹、蓝牙、超声波避障智能小车的所有关键代码和注意事项的详细分享。希望我的分享对你有所帮助。

最后,我将完整的代码工程分享给大家,白嫖的时候可不能忘了点赞,你的点赞,是我保持分享欲的无尽动力!!!加油,冲冲冲……

1、https://download.csdn.net/download/m0_73931287/88726542

2、https://download.csdn.net/download/m0_73931287/88726547?spm=1001.2014.3001.5503

如果以上资源不能下载,请读者下方留言或者私信我,收到后我会第一时间回复。 

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号