• stm32之智能小车总结


            作为学习stm32f103c8t6阶段的收官项目,这里做下总结,源码放在了最后。

    一、功能描述

    • 1、跟随功能
    • 2、循迹功能
    • 3、避障功能
    • 4、测速功能
    • 5、温湿度常显
    • 6、oled显示
    • 7、语音或蓝牙进行功能切换
    • 8、手势功能

    二、主要代码解析

    2.1、main

    main函数主要展示while里的功能,具体的实现在car_function文件内。

    1. int main(void)
    2. {
    3. HAL_Init();
    4. MX_GPIO_Init();
    5. MX_USART1_UART_Init();
    6. MX_TIM2_Init();
    7. MX_TIM4_Init();
    8. MX_TIM1_Init();
    9. MX_TIM3_Init();
    10. MX_I2C1_Init();
    11. init();
    12. while (1)
    13. {
    14. get_mode();
    15. reset();
    16. switch(runMode) {
    17. case tracingMode:
    18. traceing();
    19. break;
    20. case followMode:
    21. follow();
    22. break;
    23. case avoidMode:
    24. avoid();
    25. break;
    26. case stopMode:
    27. stop_car();
    28. break;
    29. }
    30. display_temp_humi();
    31. }
    32. }

    2.2、init

            初始化一个是要开启串口中断,另外要把电机旋转90让超声波正对前方,再者就是显示空数据。

    这里没有开启测速的中断TIM3,因为开启会影响DHT11的时序,导致其卡死到检测温湿度的while循环里。

    1. void init() {
    2. //开启串口中断蓝牙在用
    3. HAL_UART_Receive_IT(&huart1, &buf, 1);
    4. //开启pwm,并旋转至最前方
    5. sg90_init();
    6. //初始化oled
    7. oled_init();
    8. oled_clear_all();
    9. oled_show_string(1,2,"mode : ready");
    10. oled_show_string(2,2, "speed: 0cm/s");
    11. oled_show_string(3,2, "Temp :--.--");
    12. oled_show_string(4,2, "Temp :--.--");
    13. }

    2.3、display_temp_humi

            停止模式时,温湿度正常在main 函数里正常检测,在小车的其它模式,要限制温湿度检测的频率,否则会影响小车的运行。小车的其它模式要比温湿度优先级高。在非停止模式时,这里会计数,当计数到50w次时才会进行一次检测。

    1. void display_temp_humi() {
    2. // 停止模式时正常检测湿度,非停止模式,计数50w检测一次
    3. if(runMode != stopMode) {
    4. count ++;
    5. if(count <= 500000) { return; }
    6. count = 0;
    7. }
    8. count = 0;
    9. //记得关中断,否则会影响DHT11采集数据
    10. HAL_TIM_Base_Stop_IT(&htim3);
    11. char msg[16];
    12. uint8_t result = trig_dht();
    13. receive_data();
    14. oled_clear(4, 8, 56, 128);
    15. sprintf(msg, "Temp : %d.%d C", datas[2], datas[3]);
    16. oled_show_string(3,2,msg);
    17. sprintf(msg, "Humi : %d.%d %%", datas[0], datas[1]);
    18. oled_show_string(4,2,msg);;
    19. HAL_Delay(500);
    20. }

     2.4、changeMode

            切换电机的模式,PWM 是变速模式,主要用在寻迹模式,NORMAL是正常模式,用在跟随和避障模式。L0110s 部分引脚要接入到stm32具体pwm功能的引脚上。模式的切换要重新初始化相应的引脚。

    1. void changeMode(uint8_t m) {
    2. mode = m;
    3. if(mode == NORMAL) {
    4. HAL_TIM_PWM_Stop(&htim2,TIM_CHANNEL_1);
    5. HAL_TIM_PWM_Stop(&htim2,TIM_CHANNEL_2);
    6. init_port();
    7. } else {
    8. MX_TIM2_Init();
    9. HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_1);
    10. HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_2);
    11. HAL_Delay(500);
    12. }
    13. }

    2.5、测速

    定时器1s,查看外部中断进入了多少次。即可算出速度(当前测速模块有问题,速度偏大)

    1. void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
    2. // 展示速度
    3. //oled部分清屏
    4. sprintf(speedString, "speed:%4dcm/s", speedCnt);
    5. //old__clear_bottom_half();
    6. oled_show_string(2,2,speedString);
    7. speedCnt = 0;
    8. }
    9. void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
    10. if(GPIO_Pin != GPIO_PIN_10) {
    11. return ;
    12. }
    13. if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_10) == GPIO_PIN_RESET)
    14. speedCnt++;
    15. }

    2.6、循迹

    循迹用的是pwm模式,原理是利用红外发射的光线有没有反射回来。

    1. void traceing() {
    2. if(runMode != lastMode) {
    3. lastMode = runMode;
    4. changeMode(PWM);
    5. HAL_Delay(500);
    6. // 处理oled
    7. oled_clear_1_line();
    8. oled_show_string(1,2,"mode : trace");
    9. }
    10. if(leftTraceValue() == GPIO_PIN_RESET && rightTraceValue() == GPIO_PIN_RESET) {
    11. forward();
    12. }
    13. if(leftTraceValue() == GPIO_PIN_RESET && rightTraceValue() == GPIO_PIN_SET) {
    14. leftward();
    15. }
    16. if(leftTraceValue() == GPIO_PIN_SET && rightTraceValue() == GPIO_PIN_RESET) {
    17. rightward();
    18. }
    19. if(leftTraceValue() == GPIO_PIN_SET && rightTraceValue() == GPIO_PIN_SET) {
    20. stop();
    21. }
    22. }

    2.7、避障

    主要是利用超声波检测左前右的障碍物的距离来决定如何前进。

    1. void avoid() {
    2. if(runMode != lastMode) {
    3. lastMode = runMode;
    4. changeMode(NORMAL);
    5. // 处理oled
    6. oled_clear_1_line();
    7. oled_show_string(1,2,"mode : avoid");
    8. HAL_Delay(500);
    9. }
    10. if(dir != MIDDLE) {
    11. dir = MIDDLE;
    12. turn_90_degree();
    13. HAL_Delay(300);
    14. }
    15. disMiddle = get_distance();
    16. if(disMiddle > 35) {
    17. forward();
    18. } else if(disMiddle < 10) {
    19. backward();
    20. } else {
    21. stop();
    22. turn_180_degree();
    23. HAL_Delay(300);
    24. disLeft = get_distance();
    25. turn_90_degree();
    26. HAL_Delay(300);
    27. turn_0_degree();
    28. dir = RIGHT;
    29. HAL_Delay(300);
    30. disRight = get_distance();
    31. if(disLeft < disRight) {
    32. rightward();
    33. HAL_Delay(150);
    34. stop();
    35. }
    36. if(disLeft > disRight){
    37. leftward();
    38. HAL_Delay(150);
    39. stop();
    40. }
    41. }
    42. }

    2023-10-2 13点 超声波中断

    1. void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
    2. //脉冲测速
    3. if(GPIO_Pin == GPIO_PIN_10) {
    4. if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_10) == GPIO_PIN_RESET)
    5. speedCnt++;
    6. }
    7. //超声波echo
    8. if(GPIO_Pin == GPIO_PIN_12) {
    9. //while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_12) == GPIO_PIN_RESET);
    10. HAL_TIM_Base_Start(&htim1);
    11. __HAL_TIM_SetCounter(&htim1, 0);
    12. while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_12) == GPIO_PIN_SET);
    13. HAL_TIM_Base_Stop(&htim1);
    14. int cnt = __HAL_TIM_GetCounter(&htim1);
    15. distance = 340 * 0.000001 * cnt * 100 / 2;
    16. }
    17. }

    2.8、跟随

    原理同循迹,so esay。

    1. void follow() {
    2. if(runMode != lastMode) {
    3. lastMode = runMode;
    4. changeMode(NORMAL);
    5. // 处理oled
    6. oled_clear_1_line();
    7. oled_show_string(1,2,"mode : follow");
    8. HAL_Delay(100);
    9. }
    10. if(leftFollowValue() == GPIO_PIN_RESET && rightFollowValue() == GPIO_PIN_RESET) {
    11. forward();
    12. }
    13. if(leftFollowValue() == GPIO_PIN_RESET && rightFollowValue() == GPIO_PIN_SET) {
    14. leftward();
    15. }
    16. if(leftFollowValue() == GPIO_PIN_SET && rightFollowValue() == GPIO_PIN_RESET) {
    17. rightward();
    18. }
    19. if(leftFollowValue() == GPIO_PIN_SET && rightFollowValue() == GPIO_PIN_SET) {
    20. stop();
    21. }
    22. }

    2.9、手势

    添加手势模块paj7620, 手势不可与DHT11共存,会影响手势的获取。

    1. void gesture() {
    2. if(runMode != lastMode) {
    3. lastMode = runMode;
    4. changeMode(NORMAL);
    5. // 处理oled
    6. oled_clear_1_line();
    7. oled_show_string(1,2,"mode : gesture");
    8. HAL_Delay(500);
    9. }
    10. paj7620_action();
    11. }
    1. void paj7620_action(void) {
    2. switch (status) {
    3. case 0x01: // up
    4. paj7620_up();
    5. break;
    6. case 0x02: // down
    7. paj7620_down();
    8. break;
    9. case 0x04: // left
    10. paj7620_left();
    11. break;
    12. case 0x08: // right
    13. paj7620_right();
    14. break;
    15. case 0x10: // push
    16. paj7620_push();
    17. break;
    18. case 0x20: // pop
    19. paj7620_pop();
    20. break;
    21. case 0x40: // rotate right
    22. paj7620_rotate_right();
    23. break;
    24. case 0x80: // rotate left
    25. paj7620_rotate_left();
    26. break;
    27. case 0x100:// wave
    28. paj7620_wave();
    29. break;
    30. case 0x00: // nothing
    31. paj7620_nothing();
    32. break;
    33. default:
    34. paj7620_error();
    35. }
    36. }

    三、效果

    stm32小车

       

    四、问题记录

    1、计数定时器(TIM3)中断影响DHT11单总线的时序,导致会卡死到检测温度的while里,导致执行不了main函数里while的其它功能(小车模式切换),但是定时器中断还是可以运行的。

    2、同理也会卡超声波while,所以开启避障模式时也要把TIM3关了。(利用外部中断,不用再关TIM)

    3、PWM模式和GPIO的功能切换,起初以为只能在初始化里使用一种模式,但在调试之后发现是可以进行转换的。

    4、oled是可以进行局部清屏的,oled清屏函数记录-CSDN博客

    5、修改蓝牙波特率,用错了指令,导致一直未成功。HC08 AT指令

    6、触发DHT11和获取数据之间不能插入printf函数,否则会卡死到 while 循环里,效果同1

    7、小车变速PWM模式时,在使用CubeMx配置引脚时,要把所有引脚输出低电平,否则PWM会不起作用。

    8、面板包接线容易松动,导致语音模式引脚输出的电平乱跳。

    9、单片机和直流电机单独供电,防止因为电机电流的问题导致单片机复位。

    10、paj7620刚开始上电之后需要复位才能正常运行,经调试在初始化的时,write时添加了300us的延时上电即可正常运行

    五、配置

    跟随功能:

    PB5 : 左红外传感器,输入模式

    PA15: 右红外传感器,输入模式

    循迹功能(原理和跟随一样):

    PB3 : 左红外传感器,输入模式

    PB4: 左红外传感器,输入模式

    避障功能:

            超声波

       PA11 : sr04中的trig引脚,输出模式

       PA12: sr04中的echo引脚,输入模式(外部中断模式)

       TIM1: 用来超声波的计时

            舵机

       TIM4(PB9): channel 4用来控制舵机的旋转角度

    温湿度功能(DHT11):

    PA8 : dht11中的Data引脚,(初始化时先配置成输入模式!!!,在需要的时候再切换成输出模式)

    Oled

    PB6(SCL) : 使用stm32 IIC1接口中的SCL

    PB7(SDA): 使用stm32 IIC1接口中的SDA

    蓝牙(hc04)

    PA9(USART1_TX) : 使用stm32 串口1中的输出

    PA10(USART1_RX): 使用stm32 串口1中的输入

    语音(su03)

    PB11: 对应su03的a25引脚,输入模式

    PB12: 对应su03的a26引脚,输入模式

    PB13: 对应su03的a27引脚,输入模式

    直流电机 和 L9110S模块

    PA0: 对应L9110s的B-1B引脚,TIM2中channle 1 的PWM模式

    PA1: 对应L9110s的A-1B引脚,TIM2中channle 2 的PWM模式

    PB0: 对应L9110s的B-1A引脚

    PB2: 对应L9110s的A-1A引脚

    l9110s中接线会影响电机的前进和后退,根据实际情况来处理引脚的配置,这里PA0 和 PA1 是控制前面的速度,PB0和PB2没有进行控制 

    测速

    PB10: 对应模块的out引脚,外部中断

    手势(paj7620)

    PA5: 对应paj7620的scl引脚,输出模式

    PA6: 对应paj7620的sda引脚,输出模式

    PA7: 对应paj7620的int引脚,外部下降沿中断

    无手势小车完整工程

    六、修订

    2023-10-2 13点:将超声波回应利用外部中断方式,这样在避障模式下,也可以进行测速

    2023-10-4 :添加手势模块

  • 相关阅读:
    2022软件测试技能 Jmeter+Ant+Jenkins持续集成并生成测试报告教程
    第一章:网络协议的奥秘
    vue+vscode 快速搭建运行调试环境与发布
    Python语言:算术运算符知识点讲解
    Linux shell 脚本学习
    Django从入门到精通:First [Django版本.Python面向对象.Web基础.创建Django项目]
    IaaS PaaS SaaS 的几大区别通俗理解
    HTTP资源预取
    世界互联网大会|美创新品发布—流动安全管控平台
    minikube 实战篇 - 镜像打包部署 - 2
  • 原文地址:https://blog.csdn.net/TSC1235/article/details/133420696