当前位置:   article > 正文

STM32智能小车(循迹、跟随、避障、测速、蓝牙、wife、4g、语音识别)总结_stm32智能跟随小车

stm32智能跟随小车

目录

1.电机模块开发

1.1 让小车动起来

1.2 串口控制小车方向

1.3 如何进行小车PWM调速

1.4 PWM方式实现小车转向

2.循迹小车 

2.1 循迹模块使用

2.2 循迹小车原理

2.3 循迹小车核心代码

2.4 循迹小车解决转弯平滑问题

3.跟随/避障小车

3.1 红外壁障模块分析​编辑

3.2 跟随小车的原理

3.3 跟随小车开发和调试代码

3.4 超声波模块介绍

3.5 舵机模块介绍

3.6 摇头避障小车开发和调试代码

4.测速小车

4.1 测速模块

4.2 测试原理和单位换算

4.3 定时器和中断实现测速开发和调试代码

4.4 小车速度显示在OLED屏

5.远程控制小车

5.1 蓝牙控制小车

5.2 蓝牙控制并测速小车

5.3 wifi控制测速小车

5.4 4g控制小车

6.语音控制小车

6.1语音模块配置:

6.2 语音控制小车开发和调试代码


1.电机模块开发

L9110s概述

接通VCC,GND 模块电源指示灯亮, 以下资料来源官方,具体根据实际调试

IA1输入高电平,IA1输入低电平,【OA1 OB1】电机正转;

IA1输入低电平,IA1输入高电平,【OA1 OB1】电机反转;

IA2输入高电平,IA2输入低电平,【OA2 OB2】电机正转;

IA2输入低电平,IA2输入高电平,【OA2 OB2】电机反转;

接线参考:

B-1A -- PA0

B-1B -- PB1

A-1A -- PA1

A-1B -- PB10 

1.1 让小车动起来

代码实现:

motor.c

  1. #include "motor.h"
  2. void goForward(void)
  3. {
  4. // 左轮
  5. HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_SET);
  6. HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_RESET);
  7. // 右轮
  8. HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET);
  9. HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET);
  10. }
  11. void goBack(void)
  12. {
  13. // 左轮
  14. HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_RESET);
  15. HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_SET);
  16. // 右轮
  17. HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);
  18. HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_SET);
  19. }
  20. void goLeft(void)
  21. {
  22. // 左轮
  23. HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_RESET);
  24. HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_RESET);
  25. // 右轮
  26. HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET);
  27. HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET);
  28. }
  29. void goRight(void)
  30. {
  31. // 左轮
  32. HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_SET);
  33. HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_RESET);
  34. // 右轮
  35. HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);
  36. HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET);
  37. }
  38. void stop(void)
  39. {
  40. // 左轮
  41. HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_SET);
  42. HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_SET);
  43. // 右轮
  44. HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);
  45. HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET);
  46. }

motor.h

  1. #ifndef __MOTOR_H__
  2. #define __MOTOR_H__
  3. #include "main.h"
  4. void goForward(void);
  5. void goBack(void);
  6. void goLeft(void);
  7. void goRight(void);
  8. void stop(void);
  9. #endif

main.c

  1. #include "motor.h"
  2. //main函数的while循环部分:
  3. while (1)
  4. {
  5. goForward();
  6. HAL_Delay(1000);
  7. goBack();
  8. HAL_Delay(1000);
  9. goLeft();
  10. HAL_Delay(1000);
  11. goRight();
  12. HAL_Delay(1000);
  13. stop();
  14. HAL_Delay(1000);
  15. }
1.2 串口控制小车方向
  • 串口分文件编程进行代码整合——通过现象来改代码
  • 接入蓝牙模块,通过蓝牙控制小车
  • 添加点动控制,如果APP支持按下一直发数据,松开就停止发数据(蓝牙调试助手的自定义按键不 能实现),就能实现前进按键按下后小车一直往前走的功能

代码实现:

usart.c

  1. #include "usart.h"
  2. #include "string.h"
  3. #include "stdio.h"
  4. #include "motor.h"
  5. //串口接收缓存(1字节)
  6. uint8_t buf=0;
  7. //定义最大接收字节数 200,可根据需求调整
  8. #define UART1_REC_LEN 200
  9. // 接收缓冲, 串口接收到的数据放在这个数组里,最大UART1_REC_LEN个字节
  10. uint8_t UART1_RX_Buffer[UART1_REC_LEN];
  11. // 接收状态
  12. // bit15, 接收完成标志
  13. // bit14, 接收到0x0d
  14. // bit13~0, 接收到的有效字节数目
  15. uint16_t UART1_RX_STA=0;
  16. #define SIZE 12
  17. char buffer[SIZE];
  18. // 接收完成回调函数,收到一个数据后,在这里处理
  19. void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
  20. {
  21. // 判断中断是由哪个串口触发的
  22. if(huart->Instance == USART1)
  23. {
  24. // 判断接收是否完成(UART1_RX_STA bit15 位是否为1)
  25. if((UART1_RX_STA & 0x8000) == 0)
  26. {
  27. // 如果已经收到了 0x0d (回车),
  28. if(UART1_RX_STA & 0x4000)
  29. {
  30. // 则接着判断是否收到 0x0a (换行)
  31. if(buf == 0x0a)
  32. {
  33. // 如果 0x0a 和 0x0d 都收到,则将 bit15 位置为1
  34. UART1_RX_STA |= 0x8000;
  35. // 灯控指令
  36. if(!strcmp(UART1_RX_Buffer, "M1"))
  37. goForward();
  38. else if(!strcmp(UART1_RX_Buffer, "M2"))
  39. goBack();
  40. else if(!strcmp(UART1_RX_Buffer, "M3"))
  41. goLeft();
  42. else if(!strcmp(UART1_RX_Buffer, "M4"))
  43. goRight();
  44. else
  45. stop();
  46. memset(UART1_RX_Buffer, 0, UART1_REC_LEN);
  47. UART1_RX_STA = 0;
  48. }
  49. else
  50. // 否则认为接收错误,重新开始
  51. UART1_RX_STA = 0;
  52. }
  53. else // 如果没有收到了 0x0d (回车)
  54. {
  55. //则先判断收到的这个字符是否是 0x0d (回车)
  56. if(buf == 0x0d)
  57. {
  58. // 是的话则将 bit14 位置为1
  59. UART1_RX_STA |= 0x4000;
  60. }
  61. else
  62. {
  63. // 否则将接收到的数据保存在缓存数组里
  64. UART1_RX_Buffer[UART1_RX_STA & 0X3FFF] = buf;
  65. UART1_RX_STA++;
  66. // 如果接收数据大于UART1_REC_LEN(200字节),则重新开始接收
  67. if(UART1_RX_STA > UART1_REC_LEN - 1)
  68. UART1_RX_STA = 0;
  69. }
  70. }
  71. }
  72. // 重新开启中断
  73. HAL_UART_Receive_IT(&huart1, &buf, 1);
  74. }
  75. }
  76. int fputc(int ch, FILE *f)
  77. {
  78. unsigned char temp[1]={ch};
  79. HAL_UART_Transmit(&huart1,temp,1,0xffff);
  80. return ch;
  81. }
1.3 如何进行小车PWM调速

原理

全速前进是LeftCon1A = 0; LeftCon1B = 1;

完全停止是LeftCon1A = 0;LeftCon1B = 0;

那么单位时间内,比如20ms, 有15ms是全速前进,5ms是完全停止, 速度就会比5ms全速前进,15ms完全停止获得的功率多,相应的速度更快!

开发:借用PWM的舵机控制代码

将控制车轮的4个 GPIO 口配置修改如下,否则小车动不起来。

原因:L9110每个控制口需要一高一低才可以动起来,如果PWM有效电平为高电平,则另一个 GPIO口则需要输出低电平才可以驱动轮子。

代码实现:

main.c

  1. // main函数里
  2. HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_1);
  3. HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_2);
  4. while (1)
  5. {
  6. __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1, 8);
  7. __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_2, 8);
  8. HAL_Delay(1000);
  9. __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1, 10);
  10. __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_2, 10);
  11. HAL_Delay(1000);
  12. __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1, 15);
  13. __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_2, 15);
  14. HAL_Delay(1000);
  15. }
1.4 PWM方式实现小车转向

右转原理: 左轮速度大于右轮

左转原理: 右轮速度大于左轮

左右轮各自调速代码实现:

  1. // main函数里
  2. while (1)
  3. {
  4. __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1,8);
  5. __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_2,15);
  6. HAL_Delay(1000);
  7. __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1,15);
  8. __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_2,8);
  9. HAL_Delay(1000);
  10. }

2.循迹小车 

2.1 循迹模块介绍
  • TCRT5000传感器的红外发射二极管不断发射红外线
  • 当发射出的红外线没有被反射回来或被反射回来但强度不够大时
  • 红外接收管一直处于关断状态,此时模块的输出端为高电平,指示二极管一直处于熄灭状态
  • 被检测物体出现在检测范围内时,红外线被反射回来且强度足够大,红外接收管饱和
  • 此时模块的输出端为低电平,指示二极管被点亮
  • 总结就是一句话,没反射回来,D0输出高电平,灭灯

接线方式

  • VCC:接电源正极(3-5V)
  • GND:接电源负极 DO:TTL开关信号输出0、1
  • AO:模拟信号输出(不同距离输出不同的电压,此脚一般可以不接)
2.2 循迹小车原理

由于黑色具有较强的吸收能力,当循迹模块发射的红外线照射到黑线时,红外线将会被黑线吸收,导致 循迹模块上光敏三极管处于关闭状态,此时模块上一个LED熄灭。在没有检测到黑线时,模块上两个LED常亮

总结就是一句话,有感应到黑线,D0输出高电平 ,灭灯

2.3 循迹小车核心代码

硬件接线

  • B-1A -- PA0
  • B-1B -- PB1
  • A-1A -- PA1
  • A-1B -- PB10
  • 循迹模块(左)--  PB3
  • 循迹模块(右) -- PB4

代码示例:

  1. #define LeftWheel_Value HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_3)
  2. #define RightWheel_Value HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_4)
  3. // main函数里
  4. while (1)
  5. {
  6. if (LeftWheel_Value == GPIO_PIN_RESET && RightWheel_Value == GPIO_PIN_RESET)
  7. goForward();
  8. if (LeftWheel_Value == GPIO_PIN_SET && RightWheel_Value == GPIO_PIN_RESET)
  9. goLeft();
  10. if (LeftWheel_Value == GPIO_PIN_RESET && RightWheel_Value == GPIO_PIN_SET)
  11. goRight();
  12. if (LeftWheel_Value == GPIO_PIN_SET && RightWheel_Value == GPIO_PIN_SET)
  13. stop();
  14. }
2.4 循迹小车解决转弯平滑问题

原理:两轮都有速度且一轮速度大于另一轮

代码实现:

  1. #define LeftWheel_Value HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_3)
  2. #define RightWheel_Value HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_4)
  3. // main函数里
  4. while (1)
  5. {
  6. if(LeftWheel_Value == GPIO_PIN_RESET && RightWheel_Value == GPIO_PIN_RESET)
  7. {
  8. __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1,19);
  9. __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_2,19);
  10. }
  11. if(LeftWheel_Value == GPIO_PIN_SET && RightWheel_Value == GPIO_PIN_RESET)
  12. {
  13. __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1,15);
  14. __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_2,8);
  15. }
  16. if(LeftWheel_Value == GPIO_PIN_RESET && RightWheel_Value == GPIO_PIN_SET)
  17. {
  18. __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1,8);
  19. __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_2,15);
  20. }
  21. if(LeftWheel_Value == GPIO_PIN_SET && RightWheel_Value == GPIO_PIN_SET)
  22. {
  23. __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1,0);
  24. __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_2,0);
  25. }
  26. }

3.跟随/避障小车

3.1 红外壁障模块分析

原理和循迹是一样的,循迹红外观朝下,跟随朝前

3.2 跟随小车的原理
  • 左边跟随模块能返回红外,输出低电平,右边不能返回,输出高电平,说明物体在左边,需要左转
  • 右边跟随模块能返回红外,输出低电平,左边不能返回,输出高电平,说明物体在右边,需要右转
3.3 跟随小车开发和调试代码

硬件接线

  • B-1A -- PB0
  • B-1B -- PB1
  • A-1A -- PB2
  • A-1B -- PB10
  • 跟随模块(左) -- PB5
  • 跟随模块(右) -- PB6

代码示例:

  1. #define LeftWheel_Value HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_5)
  2. #define RightWheel_Value HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_6)
  3. // main函数里
  4. while (1)
  5. {
  6. if(LeftWheel_Value == GPIO_PIN_RESET && RightWheel_Value == GPIO_PIN_RESET)
  7. goForward();
  8. if(LeftWheel_Value == GPIO_PIN_SET && RightWheel_Value == GPIO_PIN_RESET)
  9. goRight();
  10. if(LeftWheel_Value == GPIO_PIN_RESET && RightWheel_Value == GPIO_PIN_SET)
  11. goLeft();
  12. if(LeftWheel_Value == GPIO_PIN_SET && RightWheel_Value == GPIO_PIN_SET)
  13. stop();
  14. }
3.4 超声波模块介绍

使用超声波模块,型号:HC-SR04

  • 怎么让它发送波 Trig ,给Trig端口至少10us的高电平
  • 怎么知道它开始发了 Echo信号,由低电平跳转到高电平,表示开始发送波
  • 怎么知道接收了返回波 Echo,由高电平跳转回低电平,表示波回来了
  • 怎么算时间 Echo引脚维持高电平的时间! 波发出去的那一下,开始启动定时器 波回来的拿一下,我们开始停止定时器,计算出中间经过多少时间
  • 怎么算距离 距离 = 速度 (340m/s)* 时间/2

时序图:

3.5 舵机模块介绍

 1. 什么是舵机

如下图所示,最便宜的舵机sg90,常用三根或者四根接线,黄色为PWM信号控制 用处:垃圾桶项目开盖用、智能小车的全比例转向、摄像头云台、机械臂等 常见的有0-90°、0-180°、0-360°

2. 怎么控制舵机

向黄色信号线“灌入”PWM信号

PWM波的频率不能太高,大约50HZ,即周期=1/频率=1/50=0.02s,20ms左右

确定周期/频率:

如果周期为20ms,则 PSC=7199,ARR=199

角度控制

0.5ms-------------0度; 2.5% 对应函数中CCRx为5

1.0ms------------45度; 5.0% 对应函数中CCRx为10

1.5ms------------90度; 7.5% 对应函数中CCRx为15

2.0ms-----------135度; 10.0% 对应函数中CCRx为20

2.5ms-----------180度; 12.5% 对应函数中CCRx为25

3.6 摇头避障小车开发和调试代码

硬件接线

  • sg90 -- PB9

cubeMX配置

代码实现

sg90.c

  1. #include "sg90.h"
  2. #include "gpio.h"
  3. #include "tim.h"
  4. void initSG90(void)
  5. {
  6. HAL_TIM_PWM_Start(&htim4,TIM_CHANNEL_4); //启动定时器4
  7. __HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_4, 17); //将舵机置为90度
  8. }
  9. void sgMiddle(void)
  10. {
  11. __HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_4, 17); //将舵机置为90度
  12. }
  13. void sgRight(void)
  14. {
  15. __HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_4, 5); //将舵机置为0度
  16. }
  17. void sgLeft(void)
  18. {
  19. __HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_4, 25); //将舵机置为180度
  20. }

sg90.h

  1. #ifndef __SG90_H__
  2. #define __SG90_H__
  3. void initSG90(void);
  4. void sgMiddle(void);
  5. void sgRight(void);
  6. void sgLeft(void);
  7. #endif

main.c

  1. initSG90();
  2. HAL_Delay(1000);
  3. while (1)
  4. {
  5. sgLeft();
  6. HAL_Delay(1000);
  7. sgMiddle();
  8. HAL_Delay(1000);
  9. sgRight();
  10. HAL_Delay(1000);
  11. sgMiddle();
  12. HAL_Delay(1000);
  13. }

封装超声波传感器

超声波模块接线:

  • Trig       --    PB7
  • Echo     -- PB8

cubeMX配置

代码实现

sr04.c

  1. #include "sr04.h"
  2. #include "gpio.h"
  3. #include "tim.h"
  4. //使用TIM2来做us级延时函数
  5. void TIM2_Delay_us(uint16_t n_us)
  6. {
  7. /* 使能定时器2计数 */
  8. __HAL_TIM_ENABLE(&htim2);
  9. __HAL_TIM_SetCounter(&htim2, 0);
  10. while(__HAL_TIM_GetCounter(&htim2) < ((1 * n_us)-1) );
  11. /* 关闭定时器2计数 */
  12. __HAL_TIM_DISABLE(&htim2);
  13. }
  14. double get_distance(void)
  15. {
  16. int cnt=0;
  17. //1. Trig ,给Trig端口至少10us的高电平
  18. HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET);//拉高
  19. TIM2_Delay_us(20);
  20. HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET);//拉低
  21. //2. echo由低电平跳转到高电平,表示开始发送波
  22. //波发出去的那一下,开始启动定时器
  23. while(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_8) == GPIO_PIN_RESET);//等待输入电平拉高
  24. HAL_TIM_Base_Start(&htim2);
  25. __HAL_TIM_SetCounter(&htim2,0);
  26. //3. 由高电平跳转回低电平,表示波回来了
  27. while(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_8) == GPIO_PIN_SET);//等待输入电平变低
  28. //波回来的那一下,我们开始停止定时器
  29. HAL_TIM_Base_Stop(&htim2);
  30. //4. 计算出中间经过多少时间
  31. cnt = __HAL_TIM_GetCounter(&htim2);
  32. //5. 距离 = 速度 (340m/s)* 时间/2(计数1次表示1us)
  33. return (cnt*340/2*0.000001*100); //单位:cm
  34. }

sr04.h

  1. #ifndef __SR04_H__
  2. #define __SR04_H__
  3. double get_distance(void);
  4. #endif

main.c

  1. while (1)
  2. {
  3. if(dir != MIDDLE){
  4. sgMiddle();
  5. dir = MIDDLE;
  6. HAL_Delay(300);
  7. }
  8. disMiddle = get_distance();
  9. if(disMiddle > 35){
  10. //前进
  11. }
  12. else
  13. {
  14. //停止
  15. //测左边距离
  16. sgLeft();
  17. HAL_Delay(300);
  18. disLeft = get_distance();
  19. sgMiddle();
  20. HAL_Delay(300);
  21. sgRight();
  22. dir = RIGHT;
  23. HAL_Delay(300);
  24. disRight = get_distance();
  25. }
  26. }

封装电机驱动

代码实现:

  1. while (1)
  2. {
  3. if(dir != MIDDLE){
  4. sgMiddle();
  5. dir = MIDDLE;
  6. HAL_Delay(300);
  7. }
  8. disMiddle = get_distance();
  9. if(disMiddle > 35){
  10. //前进
  11. goForward();
  12. }else if(disMiddle < 10){
  13. goBack();
  14. }else
  15. {
  16. //停止
  17. stop();
  18. //测左边距离
  19. sgLeft();
  20. HAL_Delay(300);
  21. disLeft = get_distance();
  22. sgMiddle();
  23. HAL_Delay(300);
  24. sgRight();
  25. dir = RIGHT;
  26. HAL_Delay(300);
  27. disRight = get_distance();
  28. if(disLeft < disRight){
  29. goRight();
  30. HAL_Delay(150);
  31. stop();
  32. }
  33. if(disRight < disLeft){
  34. goLeft();
  35. HAL_Delay(150);
  36. stop();
  37. }
  38. }
  39. HAL_Delay(50);
  40. }

4.测速小车

4.1 测速模块

  • 用途:广泛用于电机转速检测,脉冲计数,位置限位等。
  • 有遮挡,输出高电平;无遮挡,输出低电平
  • 接线 :VCC 接电源正极3.3-5V
  • GND 接电源负极 DO TTL开关信号输出
  • AO 此模块不起作用
4.2 测试原理和单位换算
  • 轮子走一圈,经过一个周长,C = 2x3.14x半径= 3.14 x 直径(6.5cm)
  • 对应的码盘也转了一圈,码盘有20个格子,每经过一个格子,会遮挡(高电平)和不遮挡(低电平), 那么一个脉冲就是走了 3.14 * 6.5 cm /20 = 1.0205CM
  • 定时器可以设计成一秒,统计脉冲数,一个脉冲就是1cm
  • 假设一秒有80脉冲,那么就是80cm/s
4.3 定时器和中断实现测速开发和调试代码

测试数据通过串口发送到上位机

硬件接线

测速模块:

  • VCC -- 3.3V 不能接5V,否则遮挡一次会触发3次中断
  • OUT -- PB14

cubeMX配置

代码实现:

  1. unsigned int speedCnt;
  2. void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
  3. {
  4. if (GPIO_Pin == GPIO_PIN_14)
  5. if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_14) == GPIO_PIN_RESET)
  6. speedCnt++;
  7. }
  8. void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
  9. {
  10. printf("speed: %d\r\n", speedCnt);
  11. speedCnt = 0;
  12. }
  13. main函数里:
  14. HAL_TIM_Base_Start_IT(&htim2);
4.4 小车速度显示在OLED屏

OLED模块介绍:STM32 OLED屏幕显示详解

硬件接线

  • SCL -- PB6
  • SDA -- PB7

代码示例:

oled.c

  1. #include "oled.h"
  2. #include "i2c.h"
  3. #include "oledfont.h"
  4. void Oled_Write_Cmd(uint8_t dataCmd)
  5. {
  6. HAL_I2C_Mem_Write(&hi2c1, 0x78, 0x00, I2C_MEMADD_SIZE_8BIT,
  7. &dataCmd, 1, 0xff);
  8. }
  9. void Oled_Write_Data(uint8_t dataData)
  10. {
  11. HAL_I2C_Mem_Write(&hi2c1, 0x78, 0x40, I2C_MEMADD_SIZE_8BIT,
  12. &dataData, 1, 0xff);
  13. }
  14. void Oled_Init(void){
  15. Oled_Write_Cmd(0xAE);//--display off
  16. Oled_Write_Cmd(0x00);//---set low column address
  17. Oled_Write_Cmd(0x10);//---set high column address
  18. Oled_Write_Cmd(0x40);//--set start line address
  19. Oled_Write_Cmd(0xB0);//--set page address
  20. Oled_Write_Cmd(0x81); // contract control
  21. Oled_Write_Cmd(0xFF);//--128
  22. Oled_Write_Cmd(0xA1);//set segment remap
  23. Oled_Write_Cmd(0xA6);//--normal / reverse
  24. Oled_Write_Cmd(0xA8);//--set multiplex ratio(1 to 64)
  25. Oled_Write_Cmd(0x3F);//--1/32 duty
  26. Oled_Write_Cmd(0xC8);//Com scan direction
  27. Oled_Write_Cmd(0xD3);//-set display offset
  28. Oled_Write_Cmd(0x00);//
  29. Oled_Write_Cmd(0xD5);//set osc division
  30. Oled_Write_Cmd(0x80);//
  31. Oled_Write_Cmd(0xD8);//set area color mode off
  32. Oled_Write_Cmd(0x05);//
  33. Oled_Write_Cmd(0xD9);//Set Pre-Charge Period
  34. Oled_Write_Cmd(0xF1);//
  35. Oled_Write_Cmd(0xDA);//set com pin configuartion
  36. Oled_Write_Cmd(0x12);//
  37. Oled_Write_Cmd(0xDB);//set Vcomh
  38. Oled_Write_Cmd(0x30);//
  39. Oled_Write_Cmd(0x8D);//set charge pump enable
  40. Oled_Write_Cmd(0x14);//
  41. Oled_Write_Cmd(0xAF);//--turn on oled panel
  42. }
  43. void Oled_Screen_Clear(void){
  44. char i,n;
  45. Oled_Write_Cmd (0x20); //set memory addressing mode
  46. Oled_Write_Cmd (0x02); //page addressing mode
  47. for(i=0;i<8;i++){
  48. Oled_Write_Cmd(0xb0+i);
  49. Oled_Write_Cmd(0x00);
  50. Oled_Write_Cmd(0x10);
  51. for(n=0;n<128;n++)Oled_Write_Data(0x00);
  52. }
  53. }
  54. void Oled_Show_Char(char row,char col,char oledChar){ //row*2-2
  55. unsigned int i;
  56. Oled_Write_Cmd(0xb0+(row*2-2)); //page 0
  57. Oled_Write_Cmd(0x00+(col&0x0f)); //low
  58. Oled_Write_Cmd(0x10+(col>>4)); //high
  59. for(i=((oledChar-32)*16);i<((oledChar-32)*16+8);i++){
  60. Oled_Write_Data(F8X16[i]); //写数据oledTable1
  61. }
  62. Oled_Write_Cmd(0xb0+(row*2-1)); //page 1
  63. Oled_Write_Cmd(0x00+(col&0x0f)); //low
  64. Oled_Write_Cmd(0x10+(col>>4)); //high
  65. for(i=((oledChar-32)*16+8);i<((oledChar-32)*16+8+8);i++){
  66. Oled_Write_Data(F8X16[i]); //写数据oledTable1
  67. }
  68. }
  69. /******************************************************************************/
  70. // 函数名称:Oled_Show_Char
  71. // 输入参数:oledChar
  72. // 输出参数:无
  73. // 函数功能:OLED显示单个字符
  74. /******************************************************************************/
  75. void Oled_Show_Str(char row,char col,char *str){
  76. while(*str!=0){
  77. Oled_Show_Char(row,col,*str);
  78. str++;
  79. col += 8;
  80. }
  81. }

main.c

  1. extern uint8_t buf;
  2. unsigned int speedCnt = 0;
  3. char speedMes[24]; //主程序发送速度数据的字符串缓冲区
  4. void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
  5. {
  6. if (GPIO_Pin == GPIO_PIN_14)
  7. if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_14) == GPIO_PIN_RESET)
  8. speedCnt++;
  9. }
  10. void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
  11. {
  12. printf("speed: %d\r\n", speedCnt);
  13. sprintf(speedMes,"speed:%2d cm/s",speedCnt);//串口数据的字符串拼装,speed是格子,每个格子1cm
  14. Oled_Show_Str(2,2,speedMes);
  15. speedCnt = 0;
  16. }

5.远程控制小车

5.1 蓝牙控制小车
  • 使用蓝牙模块,串口透传
  • 蓝牙模块,又叫做蓝牙串口模块

串口透传技术:

  • 透传即透明传送,是指在数据的传输过程中,通过无线的方式这组数据不发生任何形式的改变,仿 佛传输过程是透明的一样,同时保证传输的质量,原封不动地到了最终接收者手里。
  • 以太网,蓝牙,Zigbee, GPRS 等模块玩法一样,对嵌入式程序员来说,不需要关心通讯模块内部数据 及协议栈工作原理,只要通过串口编程获得数据即可

代码实现:

整合前面串口控制小车代码,接入蓝牙模块

  1. if (!strcmp(UART1_RX_Buffer, "M1"))
  2. {
  3. goForward();
  4. HAL_Delay(10);
  5. }
  6. else if (!strcmp(UART1_RX_Buffer, "M2"))
  7. {
  8. goBack();
  9. HAL_Delay(10);
  10. }
  11. else if (!strcmp(UART1_RX_Buffer, "M3"))
  12. {
  13. goLeft();
  14. HAL_Delay(10);
  15. }
  16. else if (!strcmp(UART1_RX_Buffer, "M4"))
  17. {
  18. goRight();
  19. HAL_Delay(10);
  20. }
  21. else
  22. stop();
5.2 蓝牙控制并测速小车

原理:运用上面讲到的蓝牙模块和测速模块,将两者代码整合

5.3 wifi控制测速小车

  • Wifi模块-ESP-01s
  • 蓝牙,ESP-01s,Zigbee, NB-Iot等通信模块都是基于AT指令的设计

AT指令介绍:

  • AT指令集是从终端设备(Terminal Equipment,TE)或数据终端设备(Data Terminal Equipment,DTE)向终端适配器(Terminal Adapter,TA)或数据电路终端设备(Data Circuit Terminal Equipment,DCE)发送的。
  • 其对所传输的数据包大小有定义:即对于AT指令的发送,除AT两个字符外,最多可以接收1056个 字符的长度(包括最后的空字符)。
  • 每个AT命令行中只能包含一条AT指令;对于由终端设备主动向PC端报告的URC指示或者response 响应,也要求一行最多有一个,不允许上报的一行中有多条指示或者响应。AT指令以回车作为结 尾,响应或上报以回车换行为结尾。

硬件接线

  • 把esp8266插进串口1

使用方法:

Wifi模块-ESP-01s_esp01s波特率-CSDN博客

5.4 4g控制小车

原理:运用EC03-DNC4G通信模块

模块介绍:

  • 基于串口AT指令的开发方式
  • 有两种工作模式,默认是透传模式,通过其他方式进入AT指令模式
  • 注意插卡不要出错,下图红色位置为SIM卡状态灯,亮才是正常

代码不做修改,直接基于蓝牙小车整合, 4g模块只要做好外网透传就可以了

6.语音控制小车

6.1语音模块配置

使用SU-03T / LD3320

具体介绍看我之前写过的博客:SU-03T语音模块的使用_罗小白的干爹的博客-CSDN博客

  

6.2 语音控制小车开发和调试代码

硬件接线:

 循迹小车: 

  • 循迹模块(左) -- PB3
  • 循迹模块(右) -- PB4

 跟随小车: 

  • 跟随模块(左) -- PA8
  • 跟随模块(右) -- PA9

 避障小车: 

  • sg90:PB9
  • Trig:PA10
  • Echo:PA11

 OLED模块: 

  • SCL -- PB6
  • SDA -- PB7

 语音模块: 

  • A25 -- PA15 (跟随)
  • A26 -- PA13 (避障)
  • A27 -- PA14 (循迹)

cubeMX配置  

代码示例:

  1. #include "main.h"
  2. #include "i2c.h"
  3. #include "tim.h"
  4. #include "gpio.h"
  5. #include "sg90.h"
  6. #include "sr04.h"
  7. #include "motor.h"
  8. #include "oled.h"
  9. #define MIDDLE 0
  10. #define LEFT 1
  11. #define RIGHT 2
  12. #define BZ 1
  13. #define XJ 2
  14. #define GS 3
  15. #define LeftWheel_Value_XJ HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_3)
  16. #define RightWheel_Value_XJ HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_4)
  17. #define LeftWheel_Value_GS HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_8)
  18. #define RightWheel_Value_GS HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_9)
  19. #define A25 HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_15)
  20. #define A26 HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_13)
  21. #define A27 HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_14)
  22. void SystemClock_Config(void);
  23. char dir;
  24. void xunjiMode()
  25. {
  26. if(LeftWheel_Value_XJ == GPIO_PIN_RESET && RightWheel_Value_XJ == GPIO_PIN_RESET)
  27. goForward();
  28. if(LeftWheel_Value_XJ == GPIO_PIN_SET && RightWheel_Value_XJ == GPIO_PIN_RESET)
  29. goLeft();
  30. if(LeftWheel_Value_XJ == GPIO_PIN_RESET && RightWheel_Value_XJ == GPIO_PIN_SET)
  31. goRight();
  32. if(LeftWheel_Value_XJ == GPIO_PIN_SET && RightWheel_Value_XJ == GPIO_PIN_SET)
  33. stop();
  34. }
  35. void gensuiMode()
  36. {
  37. if(LeftWheel_Value_GS == GPIO_PIN_RESET && RightWheel_Value_GS == GPIO_PIN_RESET)
  38. goForward();
  39. if(LeftWheel_Value_GS == GPIO_PIN_SET && RightWheel_Value_GS == GPIO_PIN_RESET)
  40. goRight();
  41. if(LeftWheel_Value_GS == GPIO_PIN_RESET && RightWheel_Value_GS == GPIO_PIN_SET)
  42. goLeft();
  43. if(LeftWheel_Value_GS == GPIO_PIN_SET && RightWheel_Value_GS == GPIO_PIN_SET)
  44. stop();
  45. }
  46. void bizhangMode()
  47. {
  48. double disMiddle;
  49. double disLeft;
  50. double disRight;
  51. if(dir != MIDDLE){
  52. sgMiddle();
  53. dir = MIDDLE;
  54. HAL_Delay(300);
  55. }
  56. disMiddle = get_distance();
  57. if(disMiddle > 35){
  58. //前进
  59. goForward();
  60. }else if(disMiddle < 10){
  61. goBack();
  62. }else
  63. {
  64. //停止
  65. stop();
  66. //测左边距离
  67. sgLeft();
  68. HAL_Delay(300);
  69. disLeft = get_distance();
  70. sgMiddle();
  71. HAL_Delay(300);
  72. sgRight();
  73. dir = RIGHT;
  74. HAL_Delay(300);
  75. disRight = get_distance();
  76. if(disLeft < disRight){
  77. goRight();
  78. HAL_Delay(150);
  79. stop();
  80. }
  81. if(disRight < disLeft){
  82. goLeft();
  83. HAL_Delay(150);
  84. stop();
  85. }
  86. }
  87. HAL_Delay(50);
  88. }
  89. int main(void)
  90. {
  91. int mark = 0;
  92. HAL_Init();
  93. SystemClock_Config();
  94. MX_GPIO_Init();
  95. MX_TIM4_Init();
  96. MX_TIM2_Init();
  97. MX_I2C1_Init();
  98. initSG90();
  99. HAL_Delay(1000);
  100. dir = MIDDLE;
  101. Oled_Init();
  102. Oled_Screen_Clear();
  103. Oled_Show_Str(2,2,"-----Ready----");
  104. while (1)
  105. {
  106. //满足寻迹模式的条件
  107. if(A25 == 1 && A26 == 1 && A27 == 0){
  108. if(mark != XJ){
  109. Oled_Screen_Clear();
  110. Oled_Show_Str(2,2,"-----XunJi----");
  111. }
  112. mark = XJ;
  113. xunjiMode();
  114. }
  115. //满足跟随模式的条件
  116. if(A25 == 0 && A26 == 1 && A27 == 1){
  117. if(mark != GS){
  118. Oled_Screen_Clear();
  119. Oled_Show_Str(2,2,"-----GenSui----");
  120. }
  121. mark = GS;
  122. gensuiMode();
  123. }
  124. //满足避障模式的条件
  125. if(A25 == 1 && A26 == 0 && A27 == 1){
  126. if(mark != BZ){
  127. Oled_Screen_Clear();
  128. Oled_Show_Str(2,2,"-----BiZhang----");
  129. }
  130. mark = BZ;
  131. bizhangMode();
  132. }
  133. }
  134. }
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/456274
推荐阅读
相关标签
  

闽ICP备14008679号