当前位置:   article > 正文

基于STM32的智能小车_stm32智能小车硬件设计

stm32智能小车硬件设计

目录

一、硬件清单

二、功能实现

三、相关代码

1、让小车动起来

2、串口控制小车方向

3、串口点动控制小车

4、硬件PWM调速控制小车前进

5、硬件PWM调速控制小车左右转

6、循迹小车

7、解决平滑转弯问题

8.跟随/避障小车

9、摇头避障小车

10、小车测速数据通过串口上传到上位机

11、串口控制小车并使用Oled屏显示速度

12、Wi-Fi测速小车并本地Oled显示

13、语音控制小车


一、硬件清单

1、主控STM32F103C8T6

7044c273cf414157af12f711b1a5b1d2.png

2、L9110S电机模块

247c4523120b446c97805a61f8ffae18.png

3、超声波模块

24b67c280b92411e8b9628d8fd15ac3b.png

4、sg90舵机模块

40d9f0ea3c7c4e879a91e92557149437.png

5、oled屏

57856dcfb46f4de6a74dd6110f493d8b.png

6、测速模块

31a2a48f75c44781b831f82d5e0013e2.png

7、红外模块

30b93da795a84b4b88a1a54c83eaca50.png

8、esp8266模块(wifi模块)

337a32904783478489f1cf071f2a9601.png

9、语音模块

97e2defc061f4f07922ca313e2ffefc9.png

10、循迹模块

d5e5f23357e84b1f823427e7b48bb8eb.png

二、功能实现

左右转与前进后退:通过控制左组轮和右组轮的配合实现。pwm调速使其更平滑

循迹功能:基于光电传感器原理,通过判断黑线和白线来决定左转或者右转

跟随功能:通过超声波测出小车和跟随物的距离决定移动方向

避障功能:通过超声波检测出前方障碍物,做出改变方向的决定

语音模块控制:多种语音改变多种引脚的电平,通过语音控制实现以上多种功能的切换

old屏显示速度:将测速模块的数据通过oled屏显示

远程控制:支持蓝牙、4g以及WiFi控制小车

三、相关代码

1、让小车动起来

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

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

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

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

84ae55d9bea045f2b168e8d4ef1db91c.png

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. }

motot.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. }

2、串口控制小车方向

usart.c(在工程配置的usart.c中添加以下代码)

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

注:主函数main.c中需要将中断打开,同时在点开魔术棒将u8勾上 如下:

779d93b82d364c509493538721fbf5b8.png

  1. #include "motor.h"
  2. extern uint8_t buf;
  3. //main函数
  4. HAL_UART_Receive_IT(&huart1, &buf, 1);

3、串口点动控制小车

在usart.c车控指令处进行修改 如下:

  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();

注:这里的HAL_Delay函数为滴答定时器 若直接如上修改代码 则在串口助手中发出车控指令时会导致程序阻塞 有以下两种解决方法:

1.在主函数中调用以下函数修改滴答定时器优先级

HAL_NVIC_SetPriority(Systick_IRQn,0,0);

2.通过Cubemx配置滴答定时器优先级

b1833a84c29f46b29507d84aafd3bd82.png

4、硬件PWM调速控制小车前进

c69d4064282a4ca0832053a0a3d605cc.png

caf37676e1d4473c9e2bae0d5842401a.png

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. }

注意:此时小车轮子并不能正常改变转动速度转动,原因是L9110电机模块每个控制口需要一高一低才可以动起来,如果PWM有效电平为高电平,则另一个 GPIO口则需要输出低电平才可以驱动轮子,解决方法是将电机模块对应的GPIO口设置低电平驱动即可

a922e14882ac4d469c833c99a8719124.png

5、硬件PWM调速控制小车左右转

main.c

  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. }

6、循迹小车

循迹原理

fc30659520bb4773aa9075fe8a0d4a86.png

1.左右循迹模块都照射到白色上——红外返回——都输出低电平——指示灯亮——直走

2.左循迹模块照射到黑色跑道上——左边红外被吸收不返回——左边输出高电平——指示灯灭——需要左转

3.右循迹模块照射到黑色跑道上——右边红外被吸收不返回——右边输出高电平——指示灯灭——需要右转

这里循迹模块是一个传感器,需要获取传感器的值进行相应操作,所以Cubemx中需配置传感器对应引脚为输入模式,如下:

88865d4b357d447fb980444a9dd94bc0.png

相关代码

main.c

  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. }

7、解决平滑转弯问题

  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. }

8.跟随/避障小车

原理:

  • 左边跟随模块能返回红外,输出低电平,右边不能返回,输出高电平,说明物体在左边,需要左转
  • 右边跟随模块能返回红外,输出低电平,左边不能返回,输出高电平,说明物体在右边,需要右转

相关代码

main.c

  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. }

9、摇头避障小车

9.1 封装摇头功能

Cubemx配置

f3c108804e164ae9b4d65551f7570d85.png

上图的预分频值和重装值通过公式代入得出,这里设置周期为20ms,公式如下:

6d007f4f0f8846388eb2c81d5ae73d6e.png

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

sg90模块角度控制:

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

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

1.5ms------------90度; 7.5% 对应函数中CCRx为15(这里经过调试发现17比较接近于90°)

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

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

09b2b7e9fb5e4722b43a688c4286a36a.png

相关代码

sg90.c

  1. #include "sg90.h"
  2. #include "gpio.h"
  3. #include "tim.h"
  4. void initSG90(void)//初始化90度
  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. #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. }

9.2 封装超声波传感器

Cubrmx配置

b405e93ac1414e5f8c660764f0f01588.png

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. #define MIDDLE 0
  2. #define LEFT 1
  3. #define RIGHT 2
  4. char dir;
  5. double disMiddle;
  6. double disLeft;
  7. double disRight;
  8. while (1)
  9. {
  10. if(dir != MIDDLE){
  11. sgMiddle();
  12. dir = MIDDLE;
  13. HAL_Delay(300);
  14. }
  15. disMiddle = get_distance();
  16. if(disMiddle > 35){
  17. //前进
  18. }
  19. else
  20. {
  21. //停止
  22. //测左边距离
  23. sgLeft();
  24. HAL_Delay(300);
  25. disLeft = get_distance();
  26. sgMiddle();
  27. HAL_Delay(300);
  28. sgRight();
  29. dir = RIGHT;
  30. HAL_Delay(300);
  31. disRight = get_distance();
  32. }
  33. }

9.3 封装电机驱动

  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. }

10、小车测速数据通过串口上传到上位机

Cubemx配置

8f6d9b3cc520454790e0ebf5126fae87.png

c08a5baef2494e7cbe736420a0c396f3.png

a718dbbf176b41a8a44051dfa809126d.png

main.c

  1. //中断定时服务函数
  2. unsigned int speedCnt;
  3. void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
  4. {
  5. if (GPIO_Pin == GPIO_PIN_14)
  6. if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_14) == GPIO_PIN_RESET)
  7. speedCnt++;
  8. }
  9. void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
  10. {
  11. printf("speed: %d\r\n", speedCnt);
  12. speedCnt = 0;
  13. }
  14. main函数里:
  15. HAL_TIM_Base_Start_IT(&htim2);

11、串口控制小车并使用Oled屏显示速度

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); //éè??ò3μ??·£¨0~7£?
  49. Oled_Write_Cmd(0x00); //éè????ê??????aáDμíμ??·
  50. Oled_Write_Cmd(0x10); //éè????ê??????aáD??μ??·
  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. }

显示代码实现

  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. }

12、Wi-Fi测速小车并本地Oled显示

esp8266.c

  1. #include "esp8266.h"
  2. #include "stdio.h"
  3. //1 工作在路由模式
  4. char LYMO[] = "AT+CWMODE=2\r\n";
  5. //2 使能多链接
  6. char DLJ[] = "AT+CIPMUX=1\r\n";
  7. //3 建立TCPServer
  8. char JLFW[] = "AT+CIPSERVER=1\r\n"; // default port = 333
  9. char AT_OK_Flag = 0; //OK返回值的标志位
  10. char AT_Connect_Net_Flag = 0; //WIFI GOT IP返回值的标志位
  11. char Client_Connect_Flag = 0;
  12. void initWifi_AP(void)
  13. {
  14. printf(LYMO);
  15. while(!AT_OK_Flag) HAL_Delay(50);
  16. AT_OK_Flag = 0;
  17. printf(DLJ);
  18. while(!Client_Connect_Flag) HAL_Delay(50);
  19. AT_OK_Flag = 0;
  20. }
  21. void waitConnect(void)
  22. {
  23. printf(JLFW);
  24. while(!AT_OK_Flag) HAL_Delay(50);
  25. AT_OK_Flag = 0;
  26. }

esp8266.h

  1. #ifndef __ESP8266_H_
  2. #define __ESP8266_H_
  3. void initWifi_AP(void);
  4. void waitConnect(void);
  5. #endif

usart.c

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

main.c

  1. #include "motor.h"
  2. #include "stdio.h"
  3. #include "oled.h"
  4. #include "esp8266.h"
  5. extern uint8_t buf;
  6. extern uint8_t buf;
  7. unsigned int speedCnt = 0;
  8. char speedMes[24]; //主程序发送速度数据的字符串缓冲区
  9. //发送数据
  10. char FSSJ[] = "AT+CIPSEND=0,5\r\n";
  11. void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
  12. {
  13. if (GPIO_Pin == GPIO_PIN_14)
  14. if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_14) == GPIO_PIN_RESET)
  15. speedCnt++;
  16. }
  17. void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
  18. {
  19. printf(FSSJ);
  20. HAL_Delay(50);
  21. sprintf(speedMes,"%2dcm/s",speedCnt);//串口数据的字符串拼装,speed是格子,每个格子1cm
  22. printf(speedMes);
  23. Oled_Show_Str(2,2,speedMes);
  24. speedCnt = 0;
  25. }
  26. int main(void)
  27. {
  28. HAL_UART_Receive_IT(&huart1, &buf, 1);
  29. HAL_TIM_Base_Start_IT(&htim2);
  30. Oled_Init();
  31. Oled_Screen_Clear();
  32. HAL_Delay(1000);
  33. initWifi_AP();
  34. waitConnect();
  35. }

 这里由于定时器2中用到延时函数,所以需要通过Cubemx配置滴答定时器中断优先级,如下:

13、语音控制小车

e3127e7d7252485ea291d1786834c3b2.png

  1. #include "sg90.h"
  2. #include "sr04.h"
  3. #include "motor.h"
  4. #include "oled.h"
  5. #include "string.h"
  6. #define MIDDLE 0
  7. #define LEFT 1
  8. #define RIGHT 2
  9. #define BZ 1
  10. #define XJ 2
  11. #define GS 3
  12. #define LeftWheel_Value_XJ HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_3)
  13. #define RightWheel_Value_XJ HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_4)
  14. #define LeftWheel_Value_GS HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_8)
  15. #define RightWheel_Value_GS HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_9)
  16. #define XJ_VALUE HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_14)
  17. #define GS_VALUE HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_15)
  18. #define BZ_VALUE HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_13)
  19. char dir;
  20. void xunjiMode()
  21. {
  22. if(LeftWheel_Value_XJ == GPIO_PIN_RESET && RightWheel_Value_XJ == GPIO_PIN_RESET)
  23. goForward();
  24. if(LeftWheel_Value_XJ == GPIO_PIN_SET && RightWheel_Value_XJ == GPIO_PIN_RESET)
  25. goLeft();
  26. if(LeftWheel_Value_XJ == GPIO_PIN_RESET && RightWheel_Value_XJ == GPIO_PIN_SET)
  27. goRight();
  28. if(LeftWheel_Value_XJ == GPIO_PIN_SET && RightWheel_Value_XJ == GPIO_PIN_SET)
  29. stop();
  30. }
  31. void gensuiMode()
  32. {
  33. if(LeftWheel_Value_GS == GPIO_PIN_RESET && RightWheel_Value_GS == GPIO_PIN_RESET)
  34. goForward();
  35. if(LeftWheel_Value_GS == GPIO_PIN_SET && RightWheel_Value_GS == GPIO_PIN_RESET)
  36. goRight();
  37. if(LeftWheel_Value_GS == GPIO_PIN_RESET && RightWheel_Value_GS == GPIO_PIN_SET)
  38. goLeft();
  39. if(LeftWheel_Value_GS == GPIO_PIN_SET && RightWheel_Value_GS == GPIO_PIN_SET)
  40. stop();
  41. }
  42. void bizhangMode()
  43. {
  44. double disMiddle;
  45. double disLeft;
  46. double disRight;
  47. if(dir != MIDDLE){
  48. sgMiddle();
  49. dir = MIDDLE;
  50. HAL_Delay(300);
  51. }
  52. disMiddle = get_distance();
  53. if(disMiddle > 35){
  54. //前进
  55. goForward();
  56. }else if(disMiddle < 10){
  57. goBack();
  58. }else
  59. {
  60. //停止
  61. stop();
  62. //测左边距离
  63. sgLeft();
  64. HAL_Delay(300);
  65. disLeft = get_distance();
  66. sgMiddle();
  67. HAL_Delay(300);
  68. sgRight();
  69. dir = RIGHT;
  70. HAL_Delay(300);
  71. disRight = get_distance();
  72. if(disLeft < disRight){
  73. goRight();
  74. HAL_Delay(150);
  75. stop();
  76. }
  77. if(disRight < disLeft){
  78. goLeft();
  79. HAL_Delay(150);
  80. stop();
  81. }
  82. }
  83. HAL_Delay(50);
  84. }
  85. int main(void)
  86. {
  87. int mark = 0;
  88. HAL_Init();
  89. SystemClock_Config();
  90. MX_GPIO_Init();
  91. MX_TIM4_Init();
  92. MX_TIM2_Init();
  93. MX_I2C1_Init();
  94. initSG90();
  95. HAL_Delay(1000);
  96. dir = MIDDLE;
  97. Oled_Init();
  98. Oled_Screen_Clear();
  99. Oled_Show_Str(2,2,"-----Ready----");
  100. while (1)
  101. {
  102. if(XJ_VALUE == GPIO_PIN_RESET && GS_VALUE == GPIO_PIN_SET && BZ_VALUE ==
  103. GPIO_PIN_SET)
  104. {
  105. if(mark != XJ)
  106. {
  107. Oled_Screen_Clear();
  108. Oled_Show_Str(2,2,"-----XunJi----");
  109. }
  110. mark = XJ;
  111. xunjiMode();
  112. }
  113. //满足循迹模式的条件
  114. if(XJ_VALUE == GPIO_PIN_SET && GS_VALUE == GPIO_PIN_RESET && BZ_VALUE ==
  115. GPIO_PIN_SET)
  116. {
  117. if(mark != GS)
  118. {
  119. Oled_Screen_Clear();
  120. Oled_Show_Str(2,2,"-----GenSui----");
  121. }
  122. mark = GS;
  123. gensuiMode();
  124. }
  125. //满足避障模式的条件
  126. if(XJ_VALUE == GPIO_PIN_SET && GS_VALUE == GPIO_PIN_SET && BZ_VALUE ==
  127. GPIO_PIN_RESET)
  128. {
  129. if(mark != BZ)
  130. {
  131. Oled_Screen_Clear();
  132. Oled_Show_Str(2,2,"-----BiZhang----");
  133. }
  134. mark = BZ;
  135. bizhangMode();
  136. }
  137. HAL_Delay(50);
  138. }
  139. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/木道寻08/article/detail/767661
推荐阅读
相关标签
  

闽ICP备14008679号