你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

基于STM32的DIY遥控小船制作

[复制链接]
STMCU 发布时间:2020-10-13 15:48
基于STM32的DIY遥控小船制作
01
前言


周末带小朋友去公园玩耍,别的小孩在池塘里玩饮料瓶做的漂流船,看着他欢乐的跟着跑跳,无比羡慕的眼神,却又不能上手的小失落,又回想起儿时用泡沫板和小电机以及电池做的小船,和那时对于电驱动产生的无比兴趣,我决定升级一下儿时的装备,基于STM32给小朋友DIY一个遥控小船,使他成为公园里焦点,同时也期待他对电气控制产生一点点好奇。


基本构想如下:stm32驱动两个小电机,小电机上安装两个螺旋桨,可以实现双桨前进、后退,单桨转弯等。供电使用18650电池,通过升压放电板管理电池的充放电。遥控使用最廉价的红外遥控,来控制小船的各种动作。同时可以增加一组数码管作为输出设备。
11.png

在万能的某宝上可以淘到所有需要的材料,并且价格都十分实惠。
12.png
02
硬件模块




红外遥控模块
一般遥控玩具使用的是2.4G高频无线遥控模块,但是本着能省则省的原则,选用遥控器带接收头3.5元还包邮的HX1838红外遥控模块。这种遥控器类似电视遥控器,优点是便宜,开发简单,缺点是控制距离短,并且要像遥控电视一样指着玩具操作,这些缺陷留着下一代产品迭代时升级。
13.png

电机驱动模块
电机使用直流小电机,3-6V可驱动,使用一片L298N驱动板驱动。STM32输出PWM可调速,可正反转。
14.png
15.png 数码管显示
基于TM1637的四位数码管,用于显示一些简单的信息。TM1637是天微公司出品的LED驱动专用芯片,集成简单,开发方便。除了TM1637,天微公司还有一系列TM16XX的芯片,主要区别就是位输出和段输出个数多少。
16.png

电池和充放电模块
18650电池具有容量大,寿命长,安全性高等优点,普遍使用在充电宝,笔记本电池、仪器仪表中,甚至特斯拉的电池组也是由7000多节这样的电池组合成的。充放电线路板,主要的作用是将电池3.7V的输出电压升压到5V,供给STM32及外围电路使用,同时还具备给电池充电的功能。实际上等同于一个充电宝,充电宝的原理就是若干个18650电池并联,再用一块充放电板管理而已。
17.png
18.png
03
嵌入式软件




红外遥控器驱动
HX1838采用的是 NEC 编码格式。载波频率为38khz。
19.png

逻辑1是2.25ms,脉冲时间560us;逻辑0为1.12ms,脉冲时间560us。根据脉冲时间长短来解码。
协议示意图:
20.png
一组数据组成:
起始是9ms的高电平脉冲
4.5ms的低电平
8位地址码,低位在前
8位地址码的反码,用于校验
8位命令码,低位在前
8位命令码的反码。
需要注意的是1838红外一体接收头为了提高接受灵敏度。输入高电平,其输出的是相反的低电平。实际测量接收到的按键波形如下图,可以看到电平是相反的。
21.png

代码实现:
使用下降沿外部中断作为一次按键的检测触发,然后每个20us读取一次数据管脚,按照上述的协议逻辑,读出一个u32的数据。

  1. U8 Infrared_Receiver_Process(void)
  2. {
  3.   U16 nTime_Num = 0;
  4.   U8 nData = 0;
  5.   U8 nByte_Num = 0;
  6.   U8 nBit_Num = 0;
  7.   
  8. nTime_Num = Infrared_Receiver_GetLowLevelTime();
  9. //t0=nTime_Num;
  10. if((nTime_Num >= 500) || (nTime_Num <= 400))//9ms 数据头高电平 hx1838输入的反的
  11. {
  12.   return INFRARED_RECEIVER_ERROR;
  13. }
  14. nTime_Num = Infrared_Receiver_GetHighLevelTime();//4.5ms 低电平 hx1838输入的反的
  15. if((nTime_Num >= 250) || (nTime_Num <= 200))
  16. {
  17.   return INFRARED_RECEIVER_ERROR;
  18. }

  19. for(nByte_Num = 0; nByte_Num < 4; nByte_Num++)//4个8位码
  20. {
  21.   for(nBit_Num = 0; nBit_Num < 8; nBit_Num++)
  22.   {
  23.     nTime_Num = Infrared_Receiver_GetLowLevelTime();//560us 高电平  hx1838输入的反的 nTime_Num=28
  24.     if((nTime_Num >= 60) || (nTime_Num <= 20))
  25.     {
  26.       return INFRARED_RECEIVER_ERROR;
  27.     }

  28.     nTime_Num = Infrared_Receiver_GetHighLevelTime();//1690us(nTime_Num=84.5) 是逻辑1 560us是逻辑0 nTime_Num=28
  29.     if((nTime_Num >=60) && (nTime_Num < 100))
  30.     {
  31.       nData = 1;
  32.     }
  33.     else if((nTime_Num >=10) && (nTime_Num < 50))
  34.     {
  35.       nData = 0;
  36.     }
  37.     else
  38.     {
  39.       return INFRARED_RECEIVER_ERROR;
  40.     }
  41.    
  42.     gInfraredReceiver_Data <<= 1;
  43.     gInfraredReceiver_Data |= nData;
  44.   }
  45. }

  46. return INFRARED_RECEIVER_OK;
  47. }
复制代码


经过调试,得到遥控器的按键键值如下:

  1. <font size="3">KEY_OK = 0x38,
  2. KEY_UP = 0x18,
  3. KEY_DOWN = 0x4a,
  4. KEY_LEFT = 0x10,
  5. KEY_RIGHT = 0x5a,
  6. KEY_1 = 0xa2,
  7. KEY_2 = 0x62,
  8. KEY_3 = 0xe2,
  9. KEY_4 = 0x22,
  10. KEY_5 = 0x02,
  11. KEY_6 = 0xc2,
  12. KEY_7 = 0xe0,
  13. KEY_8 = 0xa8,
  14. KEY_9 = 0x90,
  15. KEY_STAR = 0x68,
  16. KEY_0 = 0x98,
  17. KEY_SHARP = 0x80,</font>
复制代码


另外由于NEC编码格式的红外遥控广泛的用于电视遥控,我们也可以找一个电视遥控器,把键值读出来,写入到stm的程序中,这样就可以用电视遥控器来操作小船了。


>>数码管显示驱动
TM1637与其他的一些TM芯片有一些区别,没有同步通信的STB脚,控制时序也有相应改变。
在输入数据时当CLK是高电平时,DIO上的信号必须保持不变;只有CLK上的时钟信号为低电平时,DIO上的信号才能改变。数据输入的开始条件是CLK为高电平时,DIO由高变低;结束条件是CLK为高时,DIO由低电平变为高电平。TM1637的数据传输带有应答信号ACK,当传输数据正确时,会在第八个时钟的下降沿,芯片内部会产生一个应答信号ACK将DIO管脚拉低,在第九个时钟结束之后释放DIO口线。
22.png

代码实现:

  1. <font size="3">void start(void){
  2. dio_output();
  3. GPIO_SetBits(CLK_GPIO_PORT,CLK_GPIO_PIN);
  4. GPIO_SetBits(DIO_GPIO_PORT,DIO_GPIO_PIN);
  5. Delay();
  6. GPIO_ResetBits(DIO_GPIO_PORT,DIO_GPIO_PIN);
  7. Delay();
  8. }


  9. void stop(void){
  10. dio_output();
  11. /*GPIO_ResetBits(CLK_GPIO_PORT,CLK_GPIO_PIN);
  12. Delay();*/
  13. GPIO_ResetBits(DIO_GPIO_PORT,DIO_GPIO_PIN);
  14. Delay();
  15. GPIO_SetBits(CLK_GPIO_PORT,CLK_GPIO_PIN);
  16. Delay();
  17. GPIO_SetBits(DIO_GPIO_PORT,DIO_GPIO_PIN);
  18. Delay();
  19. }


  20. void ack(void)
  21. {
  22. u8 i;
  23. dio_input();
  24. GPIO_ResetBits(CLK_GPIO_PORT,CLK_GPIO_PIN);
  25. Delay();
  26. while(readDio()==1&&(i<250))i++;
  27. GPIO_SetBits(CLK_GPIO_PORT,CLK_GPIO_PIN);
  28. Delay();
  29. GPIO_ResetBits(CLK_GPIO_PORT,CLK_GPIO_PIN);
  30. dio_output();
  31. }
  32. void write_data(u8 wr_data)
  33. {
  34. u8 i;
  35. for(i=0;i<8;i++)//开始传送8位数据,每循环一次传送一位数据
  36. {
  37.   GPIO_ResetBits(CLK_GPIO_PORT,CLK_GPIO_PIN);
  38.   if(wr_data & 0x01){
  39.   GPIO_SetBits(DIO_GPIO_PORT,DIO_GPIO_PIN);
  40.   }else{
  41.   GPIO_ResetBits(DIO_GPIO_PORT,DIO_GPIO_PIN);
  42.   }
  43.   Delay();
  44.   wr_data >>= 1;
  45.   GPIO_SetBits(CLK_GPIO_PORT,CLK_GPIO_PIN);
  46.   Delay();
  47. }
  48. ack();
  49. }
  50. void tm1637_DispStr(u8 *str)
  51. {
  52. u8 i;
  53. u8 ledCode[5];


  54. for(i = 0;i < 4;i++)
  55. {
  56. ledCode[i] = GetLedCode(str[i]);
  57. }
  58. start();
  59. write_data(0x44);    //固定地址模式
  60. stop();
  61. start();
  62. write_data(LED_GRID1);
  63. write_data(ledCode[0]);
  64. stop();
  65. start();
  66. write_data(LED_GRID2);
  67. write_data(ledCode[1]);
  68. stop();
  69. start();
  70. write_data(LED_GRID3);
  71. write_data(ledCode[2]);
  72. stop();
  73. start();
  74. write_data(LED_GRID4);
  75. write_data(ledCode[3]);
  76. stop();
  77. start();
  78. write_data(0x89);
  79. stop();
  80. }</font>
复制代码



小船控制逻辑
目前的逻辑比较简单, 就是收到按键后控制电机的转停。

  1. void dealIrKeyDown(){
  2. u8 len;
  3. len = irKeyfifo_count;
  4. if(len > 0){
  5.   irKeyDataOut = irKeyfifo_DataOut();
  6.   //按键显示
  7.   U8ToHexstr(irKeyDataOut.index,dispStr);
  8.   U8ToHexstr(irKeyDataOut.cmd,dispStr+2);
  9.   tm1637_DispStr(dispStr);
  10.   switch(irKeyDataOut.cmd){
  11.     case KEY_OK:
  12.     case KEY_OK_CHANGHONG:
  13.     motor1_Stop();
  14.     motor2_Stop();
  15.     break;
  16.     case KEY_UP:
  17.     case KEY_UP_CHANGHONG:
  18.     motor1_ForwardRun();
  19.     motor2_ForwardRun();
  20.     break;
  21.     case KEY_DOWN:
  22.     case KEY_DOWN_CHANGHONG:
  23.     motor1_BackwardRun();
  24.     motor2_BackwardRun();
  25.     break;
  26.     case KEY_LEFT:
  27.     case KEY_LEFT_CHANGHONG:
  28.     motor1_ForwardRun();
  29.     motor2_Stop();
  30.     state = SHIP_LEFT;
  31.     break;
  32.     case KEY_RIGHT:
  33.     case KEY_RIGHT_CHANGHONG:
  34.     motor2_ForwardRun();
  35.     motor1_Stop();
  36.     state = SHIP_RIGHT;
  37.     break;
  38.     default:
  39.     motor1_Stop();
  40.     motor2_Stop();
  41.     state = SHIP_STOP;
  42.     break;
  43.     }
  44.   }
  45. }
复制代码



04
DIY成品展示和演示视频
23.png

05
总结存在的问题


遥控不灵敏
毕竟红外遥控的使用场合并不是移动的物体上,再加上距离近,经常会出现遥控要按多次的情况。后期考虑改进stm的协议处理方法,增加容错性,如果还是不行就考虑升级为2.4G专用的玩具遥控。


防水性问题
正常使用不会有水进入,但是在没有大人的情况下交给小孩操作,就没那么保险了。
后期考虑升级防水性能。


没有声光电,不够酷炫。
考虑升级,增加led灯条。





收藏 评论2 发布时间:2020-10-13 15:48

举报

2个回答
STMCU 回答时间:2020-10-13 15:54:50
更换遥控器
01
前言

前面的文章《基于STM32的DIY遥控小船制作》中使用的HX1838红外遥控模块确实是足够便宜,但是实际用下来遥控距离太短,大概只有2米左右。外加必须指着才能遥控,在公共场合操作起来有些奇怪。
02
升级改造

在某宝上寻到了便宜的27Mhz四通道玩具遥控器。
11.png
接收小板供电电压4.5-6v,直接接到电池组输出的5V上。小板输出是4个控制信号,按下遥控按键后,由0V变为5V。注意信号接到stm32的ft耐5v管脚上。程序中定时扫描4个gpio的电平,当电平变化时就收到了遥控按键,控制电机做出相应的响应即可。

实际使用下来,比红外遥控器强多了,稳定性大大加强,遥控距离4-5m,可玩性提升很多。但是遥控距离还是不够远,后面再升级2.4GHz遥控器。
另外还提升防水性能,把小船原来的收纳盒盖子盖上,只露出开关和天线,即使小朋友用水枪喷小船,也算能过关。

damiaa 回答时间:2020-10-14 11:55:28
好 好 好

所属标签

STM32团队

意法半导体微控制器和微处理器拥有广泛的产品线,包含低成本的8位单片机和基于ARM® Cortex®-M0、M0+、M3、M4、M33、M7及A7内核并具备丰富外设选择的32位微控制器及微处理器


最新内容

相似技术帖

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
微信公众号二维码 微信公众号
手机版二维码 手机版