用STM32F401和nRF24L01制作无线调速小车

硬件配置

在做这个小项目前, 考察过STM32F103C8T6, STM32F401CCU6STC89C52这三个MCU, 并实际跑了一些用例

  • STC89C52在代码上要简单得多, 它的问题是没有ADC功能, 所以无法用于遥控器部分, 只能用于小车部分, 而且PWM输出是软输出, 通过主循环实现的. 带ADC功能的STC单片机型号有STC12C5A系列和STC15F, STC15W系列, 但是现在市场价格都很贵, 单单芯片就需要22RMB.
  • STM32F103C8T6的功能应对这个项目没问题, 在网上这个型号的代码特别丰富, 问题在于现在价格太贵了, 一个原装芯片的最小系统板价格为38RMB, CH版也要25RMB, 相对于高一代且系统板价格仅为15RMB的STM32F401CCU6没有优势.
  • STM32F401CCU6最小系统板价格15RMB, 功能应对这个项目没有问题, 它的劣势是可参考的资料少. STM32F4系列在国内使用的较少, 代码例子在网上少并且很多是针对F407这些高级型号的, 如果用在F401上需要额外的调整.

最终选择的是STM32F401CCU6, 代码上的问题都不是问题, 对吗?

遥控器部分

  • 电源: 3.7V 18650锂电
  • 3.3V稳压: 一个1N4148二极管
  • MCU: STM32F401CCU6最小系统板
  • 输入: 双轴摇杆模块
  • 无线: nRF24L01模块

硬件部分的说明

  • 1N4148能产生0.6V的压降, 对于3.7V的锂电来说是足够的, 实际测试18650两端电压约4.0V, 通过1N4148后输出的电压是3.2V. STM32F401CCU6, nRF24L01和双轴摇杆耗电量都非常小, 1N4148应付这个没问题.
  • 因为单节18650电池盒输出线是裸的多芯软线, 不方便直接连到模块上, 并且因为这部分两个模块nRF24L01和双轴摇杆都需要用到3.3V电压, 所以另外用万能板做了一个中转, PCB接线端子用来连接电源线, 3V3通过一个1N4148后接到3V3的排针上, 另一个排针是地线.
  • 这个接线端子是从炸掉的L9110s模块上拆的

小车部分

  • 电源: 7.4V (18650锂电x2)
  • 6V稳压: 串联两个IN4007
  • 3.3V稳压: AMS1117 3.3V模块
  • MCU: STM32F401CCU6
  • 无线: nRF24L01模块
  • 电机驱动: L9110双路x2
  • 小车底盘
  • 48:1减速电机x4

硬件部分的说明

  • 电机用的是普通的48:1减速电机, 工作电压为6V, 另外L9110s虽然标称能到12V, 市面上的L9110s大部分到不了这么高, 安全电压在7V左右, 所以这里需要将电压降到6V附近, 通过两个1N4007能产生1.2V的压降
  • 运行时每个电机的电流为0.15A, 合计0.6A, STM32F401,nRF24L01的耗电可以忽略不计, 1N4007的工作电流为1A, 应该是够的
  • STM32需要的3.3V和电源电压相差较远, 直接用AMS1117模块做降压了
  • 因为有4个电机, 所以需要两个双通道L9110s模块
  • 电池盒引出的是裸的多芯软线, 不方便直接连到模块上, 因此另外用万能板做了个中间板, 加了接线端子连接电池盒, 正极串联两个1N4007输出到正极排针, 另外还有地线排针, 以及四组排针用于L9110s的输入(各侧的两个通道共用一组PWM)

接线

遥控器部分

MCU需要的接口如下

  • UART: 方便调试
    • PA9 => USB2TTL的RX
    • PA10 => USB2TTL的TX
  • SPI: 连接nRF24L01
    • PA5,PA6,PA7, PB13,PB14,PB15
  • ADC: 两个pin, 连接双轴摇杆
    • PA0 => 摇杆AXIS X
    • PA1 => 摇杆AXIS Y
  • VCC
  • GND

与nRF24L01的接线

STM32 nRF24L01
PA4 SPI1_NSS N/A
PA5 SPI1_SCK SCK
PA6 SPI1_MISO MISO
PA7 SPI1_MOSI MOSI
PB13 IRQ
PB14 CE
PB15 CSN

小车部分

MCU需要的接口如下

  • UART: 方便调试
    • PA9,PA10, 同上
  • SPI: 连接nRF24L01
    • PA5,PA6,PA7, PB13,PB14,PB15
  • PWM: 4个pin, 输出4组PWM, 分别对应左右侧的两组 L9110
    • PA0,PA2: 左侧电机
    • PA1,PA3: 右侧电机

nRF24L01接线同上.

功能实现

遥控器部分

这一块主要是通过两个ADC通道采集摇杆电压, ADC采集使用的DMA的模式, 在主循环中定时(几十到几百毫秒)去读取电压, 并转换到[0, FF]区间, 通过nRF24L01发射出去. ADC采集电压时, 每个通道使用4个u16做缓存, 输出的数值是对这个4个数值做平均, 抑制抖动.

涉及的技术名词: UART, ADC, DMA, TIMER, SPI

小车部分

小车部分的功能有几部分:

  1. nRF24L01的中断接收. 需要将nRF24L01的接收配置为中断模式, 这样只有在遥控端发出指令时, 小车才做相应的动作, 相比在循环中检测接收信息并调整输出的实现方式更及时高效.
  2. PWM输出控制小车的速度和方向.
  3. 将接收得到的X轴Y轴向量, 映射到两路电机的方向和强度.

涉及的技术名词: UART, TIMER, EXTI, SPI, PWM
以下具体说明

nRF24L01的中断接收

这部分需要在STM32上新增一个EXTI中断源, 映射到nRF24L01的IRQ PIN脚. 这个中断是低电平触发, 注意在处理完中断后, 需要清空接收缓冲, 不然下一次还会读到旧值.
中断处理的方法内根据接收到的数值调整PWM输出, 实现遥控功能.

这里还有一个定时器TIM3, 当前设置的定时时间为0.5秒, 每次中断处理时会将定时器初始化, 在定时器经过0.5秒后触发时, 会将PWM输出归零, 归零后电机都会停止. 通过这个机制, 在遥控器发出指令后小车会在当前指令下输出PWM 0.5秒, 如果持续收到指令则持续输出, 如果未收到指令, 则在0.5秒后停止输出, 体现在小车运动上, 就是每次指令下小车会移动0.5秒.

PWM输出控制速度和方向

PWM的频率选择: 48:1减速电机是淘宝上最便宜常见的减速电机, 最佳PWM频率是25Hz-50Hz. 这个频率的来源在这里, 里面有很详细的说明和实验测试结果. 我把我关心的部分内容翻译了一下, 可以看这里. 我在实际使用中观察到的结果是符合这篇文章的结论的.

这里多说几句. 关于电机的PWM频率选择, 在网上查了很久, 得到的结果大部分是错误的, 很多人文章里写的频率是6-20KHz. 这里需要注意区分一下, 如果你用的是直流有刷电机, 那么用这么高的PWM频率是会出问题的, 建议在几十到几百Hz的范围去测试.

PWM控制速度比较好理解, 但是控制方向的具体实现需要通过两个PWM配合. 尝试过通过1路PWM+1路GPIO进行方向切换, 但是无法正常工作, 最后还是要通过两路PWM. 根据方向, 设置其中一路PWM输出为0. 这里为了避免出现双高电平(网上有很多人提到双高导致L9110s烧毁), 在程序中先设置输出为0的一路PWM, 再输出另一路不为0的PWM.

X轴Y轴向量映射到左右两路电机

这一块花了我一些时间. 在网络上找到的资料看, 实现方式更多是通过Y轴计算出左右电机整体的前进后退占空比, 然后通过X轴计算左右电机占空比差值, 再将这两个结果叠加, 得到最后的左右电机占空比. 这个计算方式的问题是当工作点在Y轴区间两端的时候, 此时叠加的差值会使Y轴的值超出区间, 但是实际上这个数值是不可能的, 所以要么将两个通道的数值都往回拉, 要么就忽略Y轴超出区间的部分, 都不是很合理.

我使用的计算方式, 是先规定摇杆圆周4个方向上对应LR通道的值:

  • 0° => L:FF, R:-FF
  • 90° => L:FF, R:FF
  • 180° => L:-FF, R:FF
  • 270° => L-FF, R:-FF

将摇杆得到的XY轴的值做成向量, 将这个向量投影为圆周上某一点, 再根据圆周上这个点两端的值计算当前点的LR值.

因为摇杆得到的XY轴空间, 实际上是一个正方形, 将其映射到圆上时, 有一个有趣的现象, 当角度位于0°到45°时, 向量的长度等于X轴的值, 而在45°到90°时, 向量长度等于Y轴的值, 这个使得计算简便了许多.

遇到的问题

L9110s发热烧毁

电源为两节18650, 电压为3.7x2=7.4V, 两路pwm输出, 当从0,0 -> 0,全速时, 电机无动作, L9110s发烫然后冒烟烧毁. 这个直接导致两个模块各烧了一片L9110s. 于是上网查相关的资料

相关的讨论

因为模块已经带了输出电容和上拉电阻, 所以
可能的原因是

  1. 电机启动电流过大导致模块烧毁. 电机静态电阻为6.5Ω, 电压7.4V时电流超过1A,
    应对方案: 串联一个5Ω的限流电阻, 可以将电流降到7.4/(6.5+5)=0.64A, 避免超出L9110s的最大电流, 运转中的电机阻抗为40Ω - 45Ω, 此时电阻上的分压不到1V, 影响不大.
  2. L9110s耐压超限. 有人说最高到6.5V.
    应对方案: 在L9110s输入电压前串联2个1N4007, 将电压降到7.4-1.4=6V, 串联2个时,启动电流1A,正向电阻0.7Ω, 空转时0.15A,正向电阻5Ω
  3. PWM频率过高. 过高的PWM频率会导致电机在低占空比时无法启动,
  4. PWM同时输出高电平

最终解决方案

  1. 串联两个1N4007将电压降到6.2V
  2. PWM频率降到100Hz

有些占空比下电机不动

在逐渐增大占空比的过程中, 有些值下电机不转, 能听到吱吱声, 如果手摸着L9110s芯片, 能感觉到此时有一阵发烫, 所以此时电流到位了, 但是没能驱动电机.

这个原因和前一个问题是一样的, 因为PWM频率过高(17.5KHz), 无法驱动电机, 在将频率降到100Hz后这个问题就没再出现.

小车在运行一段时间后中断灯常亮, 失去响应

经过检查, 是因为在处理nRF24L01接收中断时, 加入了一个延时1ms的处理, 会卡在这个延时函数上, 将这个延时处理删除后就未再出现这个情况

参考

原文地址:https://www.cnblogs.com/milton/p/15087381.html