硬件配置
在做这个小项目之前, 考察过STM32F103C8T6
, STM32F401CCU6
和STC89C52
这三个MCU, 实际上跑了一些用例
- STC89C52在代码上要简单得多, 没有问题ADC功能, 因此不能用于遥控部分, 只能用于小车, 而且PWM输出为软输出, 主循环实现. 带ADC功能的STC单片机型号有STC12C5A系列和STC15F, STC15W系列, 但现在市场价格很贵, 单芯片需要22RMB.
- STM32F103C8T该项目的功能处理没有问题, 这种类型的代码在网上特别丰富, 问题是现在价格太贵了, 原装芯片最小系统板价格为38RMB, CH版也要25RMB, 与高一代相比,系统板的价格仅为15RMB的STM32F401CCU6没有优势.
- STM32F401CCU最小系统板价156RMB, 该项目的功能响应没有问题, 其缺点是参考资料少. STM32F4系列在国内使用较少, 网上代码例子少,很多都是针对的F407这些高级型号, 如果用在F401需要额外的调整.
最后的选择是STM32F401CCU6, 代码问题不是问题, 对吗?
遥控器部分

- 电源: 3.7V 18650锂电
- 3.3V稳压: 一个1N4148二极管
- MCU: STM32F401CCU6最小系统板
- 输入: 双轴摇杆模块
- 无线: nRF24L01模块
说明硬件部分
- 1N4148能产生0.6V的压降, 对于3.7V锂电池就够了, 实际测试1865年两端电压约4.0V, 通过1N4148后输出电压为3.2V. STM32F401CCU6, nRF24L01和双轴摇杆的功耗都很小, 1N应付4148没问题.
- 由于单节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模块降压。
- 因为有四个电机, 因此需要两个双通道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做缓存, 输出值平均为这四个值, 抑制抖动.
涉及的技术术语: UART, ADC, DMA, TIMER, SPI
小车部分
汽车功能有几个部分:
- nRF24L01的中断接收. 需要将nRF24L01的接收配置为中断模式, 只有在遥控端发出指令时, 小车才做相应的动作, 与在循环中检测接收信息并调整输出的实现方法相比,更及时、更高效.
- PWM输出控制车辆的速度和方向.
- 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. 于是上网查相关的资料
- Is this the reason for burning my h-bridge? https://www.eevblog.com/forum/beginners/is-this-the-reason-for-burning-my-h-bridge/
- L9110 IC goes up in smoke https://forum.arduino.cc/t/l9110-ic-goes-up-in-smoke/367873
- L9110 up in smoke https://forum.arduino.cc/t/l9110-up-in-smoke/381488
- https://forum.arduino.cc/t/fried-my-mega-and-multiple-dc-motor-control-boards-how-can-i-prevent-this/664303
- AB全高状态,静态加电时AB=HH没有发热,但是给1kHz脉冲,9110就冒烟.正常驱动A=H,B=L时,B变H感觉有短时间刹车现象,因为如果A=0,电机停得慢,当A=H,B由L变H就快速停.按理AB=HH是可以的,只是实际结果,静态没问题,动态就烧了 https://www.amobbs.com/thread-4986052-1-1.html
- https://www.zhihu.com/question/52548517
- https://www.icxbk.com/ask/detail?tid=30203
- 对直流电机选择最优的PWM频率 https://learn.adafruit.com/improve-brushed-dc-motor-performance?view=all
因为模块已经带了输出电容和上拉电阻, 所以
- 电机启动电流过大导致模块烧毁. 电机静态电阻为6.5Ω, 电压7.4V时电流超过1A, 应对方案: 串联一个5Ω的限流电阻, 可以将电流降到7.4/(6.5+5)=0.64A, 避免超出L9110s的最大电流, 运转中的电机阻抗为40Ω - 45Ω, 此时电阻上的分压不到1V, 影响不大.
- L9110s耐压超限. 有人说最高到6.5V. 应对方案: 在L9110s输入电压前串联2个1N4007, 将电压降到7.4-1.4=6V, 串联2个时,启动电流1A,正向电阻0.7Ω, 空转时0.15A,正向电阻5Ω
- PWM频率过高. 过高的PWM频率会导致电机在低占空比时无法启动,
- PWM同时输出高电平
- 串联两个1N4007将电压降到6.2V
- PWM频率降到100Hz
有些占空比下电机不动
在逐渐增大占空比的过程中, 有些值下电机不转, 能听到吱吱声, 如果手摸着L9110s芯片, 能感觉到此时有一阵发烫, 所以此时电流到位了, 但是没能驱动电机.
这个原因和前一个问题是一样的, 因为PWM频率过高(17.5KHz), 无法驱动电机, 在将频率降到100Hz后这个问题就没再出现.
小车在运行一段时间后中断灯常亮, 失去响应
经过检查, 是因为在处理nRF24L01接收中断时, 加入了一个延时1ms的处理, 会卡在这个延时函数上, 将这个延时处理删除后就未再出现这个情况
参考
- 直流电机的性能优化 https://learn.adafruit.com/improve-brushed-dc-motor-performance?view=all