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

基于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
好 好 好

所属标签

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