当前位置:   article > 正文

从零开始速通一台多方式控制的STM32自平衡小车【2蓝牙通信篇】_自平衡小车如何加入蓝牙功能

自平衡小车如何加入蓝牙功能

 前言  

      这个学期有个嵌入式课程设计的任务,要求用战舰V3开发板整点活出来。毫无嵌入式底子但是想搞点花头的我思来想去,最终决定搞一台多方式控制的STM32自平衡小车。

        整个设计可以简单的分为遥控器部分和小车部分。遥控器用的是正点原子STM32F1战舰V3开发板;小车底盘、主控板用的是大鱼电子家的。目前已经实现:

        用战舰V3的按键控制;

        游戏手柄连接战舰V3控制;

        MPU6050连接战舰V3实现体感控制;

        键盘控制(电脑蓝牙连接小车然后键盘控制小车运动)。

        演示视频链接:【疯狂试验】超越想象!我用STM32打造的自平衡小车竟然能飞!?_哔哩哔哩_bilibili


蓝牙通信篇

        如何实现远程控制小车呢?其中一项较为简单的实现方式就是蓝牙。在控制器端串口连接主机蓝牙,在小车端串口连接从机蓝牙。在控制器端,将控制指令信息通过串口发送给主机蓝牙模块,主机蓝牙通过蓝牙通信协议将信息传输到从机蓝牙,小车端通过串口中断获取从机蓝牙接受的数据,进而控制小车。

主从蓝牙配对

        首先我们需要一个能当主机的蓝牙和一个能当从机的蓝牙。注意:两个只能当从机的蓝牙不能进行配对!所以购买蓝牙模块时请注意其中一个蓝牙是否能当主机蓝牙。我用的主机蓝牙是HC05,从机蓝牙是BT06,两个加起来花我了¥24。

        主从蓝牙配对需要更改蓝牙的初始化信息。修改可以采用串口调试工具例如XCOM进行可视化更改。当然更改前需要将蓝牙与电脑相连。一般是蓝牙接TTL转USB接口工具,接口工具USB口接电脑。蓝牙有4引脚或6引脚的,但是只需要用到其中4个引脚:VCC接5VGND接地RXD接TXDTXD接RXD

         连接好打开XCOM是如下界面,当你正确连接蓝牙、TTL转USB接口工具和电脑时,串口选择处会有跳出串口信息,点击打开串口,切换到多条发送,发送AT指令来修改蓝牙信息。注意:不同型号的蓝牙AT指令集不同(一般是一个文档),具体请参考你所采用的蓝牙型号

        HC05有点特殊,进入设置模式需要按住模块的小按键插电,看到红灯闪烁慢,那么说明工作进入设置模式。

        我们需要修改的AT信息可以参考这位佬的博客,他写的很详细。蓝牙的ROLE、PSWD、MODE、BIND等参数需要修改:(2条消息) HC05蓝牙主机配对BT06蓝牙从机教程_bt06蓝牙程序流程图_Diss_chan的博客-CSDN博客

       当主从蓝牙配置好后,可以给蓝牙通电连接。先启动从机蓝牙BT06再启动HC05,指示灯闪烁一致时即表示成功。

主机蓝牙与战舰V3开发板的连接和代码

        连接示意图如下。RXD接PB10(TX)TXD接PB9(RX),VCC接5v,GND接地。当然可以选择连接到其他引脚。

         以下是usart2.c代码。

  1. #include "delay.h"
  2. #include "usart2.h"
  3. #include "stdarg.h"
  4. #include "stdio.h"
  5. #include "string.h"
  6. #include "timer.h"
  7. //
  8. //本程序只供学习使用,未经作者许可,不得用于其它任何用途
  9. //ALIENTEK STM32开发板
  10. //串口3驱动代码
  11. //正点原子@ALIENTEK
  12. //技术论坛:www.openedv.com
  13. //修改日期:2015/3/29
  14. //版本:V1.0
  15. //版权所有,盗版必究。
  16. //Copyright(C) 广州市星翼电子科技有限公司 2009-2019
  17. //All rights reserved
  18. //
  19. //串口接收缓存区
  20. u8 USART2_RX_BUF[USART2_MAX_RECV_LEN]; //接收缓冲,最大USART2_MAX_RECV_LEN个字节.
  21. u8 USART2_TX_BUF[USART2_MAX_SEND_LEN]; //发送缓冲,最大USART2_MAX_SEND_LEN字节
  22. //通过判断接收连续2个字符之间的时间差不大于10ms来决定是不是一次连续的数据.
  23. //如果2个字符接收间隔超过10ms,则认为不是1次连续数据.也就是超过10ms没有接收到
  24. //任何数据,则表示此次接收完毕.
  25. //接收到的数据状态
  26. //[15]:0,没有接收到数据;1,接收到了一批数据.
  27. //[14:0]:接收到的数据长度
  28. vu16 USART2_RX_STA=0;
  29. void USART2_IRQHandler(void)
  30. {
  31. u8 res;
  32. if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)//接收到数据
  33. {
  34. res =USART_ReceiveData(USART2);
  35. if((USART2_RX_STA&(1<<15))==0)//接收完的一批数据,还没有被处理,则不再接收其他数据
  36. {
  37. if(USART2_RX_STA<USART2_MAX_RECV_LEN) //还可以接收数据
  38. {
  39. TIM_SetCounter(TIM7,0);//计数器清空 //计数器清空
  40. if(USART2_RX_STA==0) //使能定时器7的中断
  41. {
  42. TIM_Cmd(TIM7,ENABLE);//使能定时器7
  43. }
  44. USART2_RX_BUF[USART2_RX_STA++]=res; //记录接收到的值
  45. }else
  46. {
  47. USART2_RX_STA|=1<<15; //强制标记接收完成
  48. }
  49. }
  50. }
  51. }
  52. //初始化IO 串口2
  53. //pclk1:PCLK1时钟频率(Mhz)
  54. //bound:波特率
  55. void USART2_init(u32 bound)
  56. {
  57. NVIC_InitTypeDef NVIC_InitStructure;
  58. GPIO_InitTypeDef GPIO_InitStructure;
  59. USART_InitTypeDef USART_InitStructure;
  60. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // GPIOB时钟
  61. RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE); //串口2时钟使能
  62. USART_DeInit(USART2); //复位串口2
  63. //USART2_TX PA2
  64. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; //PB10
  65. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  66. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
  67. GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化PA2
  68. //USART2_RX PA3
  69. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
  70. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
  71. GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化PA3
  72. USART_InitStructure.USART_BaudRate = bound;//波特率一般设置为9600;
  73. USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
  74. USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
  75. USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
  76. USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
  77. USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式
  78. USART_Init(USART2, &USART_InitStructure); //初始化串口 3
  79. USART_Cmd(USART2, ENABLE); //使能串口
  80. //使能接收中断
  81. USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);//开启中断
  82. //设置中断优先级
  83. NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
  84. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
  85. NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3
  86. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
  87. NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
  88. TIM7_Int_Init(99,7199); //10ms中断
  89. USART2_RX_STA=0; //清零
  90. TIM_Cmd(TIM7,DISABLE); //关闭定时器7
  91. }
  92. //串口2,printf 函数
  93. //确保一次发送数据不超过USART2_MAX_SEND_LEN字节
  94. void u2_printf(char* fmt,...)
  95. {
  96. u16 i,j;
  97. va_list ap;
  98. va_start(ap,fmt);
  99. vsprintf((char*)USART2_TX_BUF,fmt,ap);
  100. va_end(ap);
  101. i=strlen((const char*)USART2_TX_BUF); //此次发送数据的长度
  102. for(j=0;j<i;j++) //循环发送数据
  103. {
  104. while(USART_GetFlagStatus(USART2,USART_FLAG_TC)==RESET); //循环发送,直到发送完毕
  105. USART_SendData(USART2,USART2_TX_BUF[j]);
  106. }
  107. }

以下是usart2.h代码。

  1. #ifndef __USART2_H
  2. #define __USART2_H
  3. #include "sys.h"
  4. #define USART2_MAX_RECV_LEN 600 //�����ջ����ֽ���
  5. #define USART2_MAX_SEND_LEN 600 //����ͻ����ֽ���
  6. #define USART2_RX_EN 1 //0,������;1,����.
  7. extern u8 USART2_RX_BUF[USART2_MAX_RECV_LEN];
  8. extern u8 USART2_TX_BUF[USART2_MAX_SEND_LEN];
  9. extern vu16 USART2_RX_STA;
  10. void usart2_init(u32 bound);
  11. void u2_printf(char* fmt,...);
  12. #endif

 main.c的代码根据需求创作了,我就简单贴一份按键&游戏手柄控制的main代码作为参考。

  1. #include "led.h"
  2. #include "delay.h"
  3. #include "key.h"
  4. #include "sys.h"
  5. #include "lcd.h"
  6. #include "usart.h"
  7. #include "usart2.h"
  8. #include "string.h"
  9. #include "key.h"
  10. #include "joypad.h"
  11. const u8*JOYPAD_SYMBOL_TBL[8]=
  12. {"Right","Left","Down","Up","Start","Select","B","A"};
  13. const char JOYPAD_KEY[8]={'C','G','E','A','Z','Z','F','D'};
  14. int main(void)
  15. {
  16. u8 t;
  17. u8 i=0;
  18. u8 key;
  19. u8 temp; //i-4
  20. u8 padKey; //joypad key
  21. u8 sendmask=0;
  22. u8 sendcnt=0;
  23. u8 sendbuf[20];
  24. u8 reclen=0;
  25. //char d='A';
  26. int te=0;
  27. delay_init(); //延时函数初始化
  28. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
  29. uart_init(115200); //串口初始化为9600
  30. USART2_init(9600);
  31. LED_Init(); //初始化与LED连接的硬件接口
  32. KEY_Init(); //初始化按键
  33. LCD_Init(); //初始化LCD
  34. JOYPAD_Init();
  35. //usmart_dev.init(72); //初始化USMART
  36. POINT_COLOR=RED;
  37. LCD_ShowString(30,30,200,16,16,"ALIENTEK STM32F1 ^_^");
  38. LCD_ShowString(30,50,200,16,16,"HC05 BLUETOOTH COM TEST");
  39. LCD_ShowString(30,70,200,16,16,"ATOM@ALIENTEK");
  40. delay_ms(1000); //等待蓝牙模块上电稳定
  41. while(1)
  42. {
  43. if(t==0){
  44. sprintf((char*)sendbuf,"A/0");
  45. u2_printf("A/0"); //发送到蓝牙模块
  46. t=1;
  47. }
  48. //sprintf((char*)sendbuf,"%c",d);
  49. //u2_printf("%c",d); //发送到蓝牙模?
  50. //joypad
  51. padKey = JOYPAD_Read();
  52. if(padKey)
  53. {
  54. LCD_ShowNum(116,130,padKey,3,16);
  55. for(i=0;i<8;i++)
  56. {
  57. if(padKey&(0X80>>i))
  58. {
  59. LCD_Fill(30+50,150,30+56+48,150+16,WHITE);
  60. LCD_ShowString(30+56,150,200,16,16,(u8*)JOYPAD_SYMBOL_TBL[i]);//????
  61. //transfer to hc05
  62. temp = JOYPAD_KEY[i];
  63. sprintf((char*)sendbuf,"%c",temp);
  64. u2_printf("%c",temp); //发送到蓝牙模块
  65. LED0=!LED0;
  66. }
  67. }
  68. }
  69. delay_ms(10);
  70. //hc05
  71. key=KEY_Scan(0);
  72. if(key==KEY1_PRES) //切换模块主从设置
  73. {
  74. sendmask=!sendmask; //发送/停止发送
  75. if(sendmask==0)LCD_Fill(30+40,160,240,160+16,WHITE);//清除显示
  76. sendcnt ='Z';
  77. }else if(key==KEY0_PRES)
  78. {
  79. sendmask=!sendmask; //发送/停止发送
  80. if(sendmask==0)LCD_Fill(30+40,160,240,160+16,WHITE);//清除显示
  81. sendcnt = 'C';
  82. }else if(key==KEY2_PRES)
  83. {
  84. sendmask=!sendmask; //发送/停止发送
  85. if(sendmask==0)LCD_Fill(30+40,160,240,160+16,WHITE);//清除显示
  86. sendcnt = 'G';
  87. }
  88. else if(key==WKUP_PRES)
  89. {
  90. sendmask=!sendmask; //发送/停止发送
  91. if(sendmask==0)LCD_Fill(30+40,160,240,160+16,WHITE);//清除显示
  92. sendcnt = 'A';
  93. }
  94. else delay_ms(10);
  95. if(t==10)
  96. {
  97. if(sendmask) //定时发送
  98. {
  99. sprintf((char*)sendbuf,"%c",sendcnt);
  100. LCD_ShowString(30+40,160,200,16,16,sendbuf); //显示发送数据
  101. u2_printf("%c",sendcnt); //发送到蓝牙模块
  102. //sendcnt++;
  103. //if(sendcnt>99)sendcnt=0;
  104. }
  105. t=0;
  106. LED0=!LED0;
  107. }
  108. if(USART2_RX_STA&0X8000) //接收到一次数据了
  109. {
  110. LCD_Fill(30,200,240,320,WHITE); //清除显示
  111. reclen=USART2_RX_STA&0X7FFF; //得到数据长度
  112. USART2_RX_BUF[reclen]=0; //加入结束符
  113. if(reclen==9||reclen==8) //控制DS1检测
  114. {
  115. if(strcmp((const char*)USART2_RX_BUF,"+LED1 ON")==0)LED1=0; //打开LED1
  116. if(strcmp((const char*)USART2_RX_BUF,"+LED1 OFF")==0)LED1=1;//关闭LED1
  117. }
  118. LCD_ShowString(30,200,209,119,16,USART2_RX_BUF);//显示接收到的数据
  119. USART2_RX_STA=0;
  120. }
  121. t++;
  122. }
  123. }

主从蓝牙发送数据测试

        测试思路是先将主机蓝牙连接到控制器即战舰V3上,从机蓝牙通过TTL-SUB接到电脑上,然后再打开XCOM上,待蓝牙连接后查看数据接收情况。当XCOM成功显示传输的数据,基本就成功了。

        注意:设置好的主机蓝牙和从机蓝牙上电后会自动匹配!XCOM记得打开串口!接收到的字符和数字信息是不同的,例如A和65!

小车端串口代码

       小车端的蓝牙模块连接主控板上的usart2,同样只需要连4条线,VCC接5V、GND、RXD接TX,TXD接RX。中断函数内容在void USART2_IRQHandler(void)中。串口接收到数据会改变标志位,通过这个条件判断从机蓝牙是否接收到主机蓝牙的数据,可以写个if,写接下来的动作。

  1. #include "usart2.h"
  2. u8 USART2_RX_BUF[USART2_MAX_RECV_LEN]; //????,??USART2_MAX_RECV_LEN???.
  3. u8 USART2_TX_BUF[USART2_MAX_SEND_LEN]; //????,??USART2_MAX_SEND_LEN??
  4. vu16 USART2_RX_STA=0;
  5. void uart2_init(u32 bound)
  6. {
  7. //GPIO端口设置
  8. GPIO_InitTypeDef GPIO_InitStructure;
  9. USART_InitTypeDef USART_InitStructure;
  10. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能UGPIOB时钟
  11. RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); //使能USART2时钟
  12. //USART2_TX
  13. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; //PA2
  14. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  15. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
  16. GPIO_Init(GPIOA, &GPIO_InitStructure);
  17. //USART2_RX
  18. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;//PA3
  19. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
  20. GPIO_Init(GPIOA, &GPIO_InitStructure);
  21. //USART 初始化设置
  22. USART_InitStructure.USART_BaudRate = bound;//串口波特率
  23. USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
  24. USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
  25. USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
  26. USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
  27. USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式
  28. USART_Init(USART2, &USART_InitStructure); //初始化串口2
  29. USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);//开启串口接受中断
  30. USART_Cmd(USART2, ENABLE); //使能串口2
  31. }
  32. /**************************************************************************
  33. 函数功能:串口2接收中断
  34. 入口参数:无
  35. 返回 值:无
  36. **************************************************************************/
  37. u8 Fore,Back,Left,Right;
  38. void USART2_IRQHandler(void)
  39. {
  40. int Uart_Receive;
  41. if(USART_GetITStatus(USART2,USART_IT_RXNE)!=RESET)//接收中断标志位拉高
  42. {
  43. Uart_Receive=USART_ReceiveData(USART2);//保存接收的数据
  44. BlueData=Uart_Receive;
  45. BluetoothCMD(Uart_Receive);
  46. }
  47. }
  48. void BluetoothCMD(int Uart_Receive)
  49. {
  50. switch(Uart_Receive)
  51. {
  52. case 65://前进
  53. Fore=1,Back=0,Left=0,Right=0;
  54. break;
  55. default://停止
  56. Fore=0,Back=0,Left=0,Right=0;
  57. break;
  58. }
  59. }
  60. void Uart2SendByte(char byte) //串口发送一个字节
  61. {
  62. USART_SendData(USART2, byte); //通过库函数 发送数据
  63. while( USART_GetFlagStatus(USART2,USART_FLAG_TC)!= SET);
  64. //等待发送完成。 检测 USART_FLAG_TC 是否置1; //见库函数 P359 介绍
  65. }
  66. void Uart2SendBuf(char *buf, u16 len)
  67. {
  68. u16 i;
  69. for(i=0; i<len; i++)Uart2SendByte(*buf++);
  70. }
  71. void Uart2SendStr(char *str)
  72. {
  73. u16 i,len;
  74. len = strlen(str);
  75. for(i=0; i<len; i++)Uart2SendByte(*str++);
  76. }

        多方式控制小车的关键之一就在于成功建立蓝牙通信!

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

闽ICP备14008679号