问题发生环境:
Arduino UNO R3控制板,用两个L298N驱动板驱动4轮小车,然后通过控制4路PWM来控制4个轮子的速度,遥控方式为红外遥控,使用的红外库是IRremote Arduino Library v2.2.3。
问题发生时,4路PWM的控制管脚分别是:PIN5,PIN6和PIN10,PIN11
问题描述:
对于PIN11,除了PWM设置为255,其他任何合法PWM值都不能驱动电机转动,其他3个PIN脚控制的电机工作正常。
定位发现是irrecv.enableIRIn()这个操作导致。也就是说IR库和PWM控制有冲突。
解决方案:
方案一(推荐):将4路PWM的控制管脚改为:PIN5,PIN6和PIN9, PIN10
方案二:根据网上的其他方案,https://www.douban.com/note/542007770/
按照下面修改boarddefs.h,改用Timer1。
// Arduino Duemilanove, Diecimila, LilyPad, Mini, Fio, Nano, etc
#else
#define IR_USE_TIMER1 // tx = pin 9
//#define IR_USE_TIMER2 // tx = pin 3
这个方案对我的方案没用,因为PIN10也要用来输出PWM:(,另外,和其他库还有编译冲突,就没有沿这条路往下走了。
根因分析:
首先,Arduino PWM控制是需要使用内部timer的,我这个方案用两个timer来控制PWM有点奢侈,其实只用PIN5 PIN6控制小车左右侧轮子的速度就可以了。但是手头的4个电机一致性不好,同样PWM设置下转速不同,只好分别控制,根本原因是穷,买不起4个一致性好的电机。
Arduino主芯片(328P)有3个timer:
Timer0 :8-bit timer, 最大值 255. timer0控制的管脚是PIN5, PIN6,delay() 和millis()也会用到Timer0;
Timer1 :16-bit timer, 最大值 65535 (unsigned 16-bit integer). timer1控制管脚PIN9,PIN10,Arduino Servo 用这个 timer;
Timer2 : 8-bit timer, timer2控制管脚PIN3和PIN11,Arduino tone()会用到;
每个timer可以有多种用途,但是每个timer只产生一个中断。
以Timer0为例,根据delay()和delay_us()的设定时间产生中断,也被用来在PIN5和PIN6上产生PWM。之所以在没产生冲突是因为PWM不用中断,而是用的比较器。
IRremote库默认使用Timer2,按照上面的说法,既然PWM不使用中断,应该不会出问题的,但是PIN11上确实没有输出。
为什么在PIN11上就出问题了?
分析了一下IR库的代码,在IRremote.cpp中ISR函数开头有以下操作:
ISR (TIMER_INTR_NAME)
{
TIMER_RESET;
从这里看,Timer2每次产生中断,都会重置timer计数,而比较器在等待达到某个计数,但是却永远等不到了:(。这就是PWM在PIN11上无法输出的原因。
至于为什么在255这个值上是可以的,从代码上看:
void analogWrite(uint8_t pin, int val)
{
// We need to make sure the PWM output is enabled for those pins
// that support it, as we turn it off when digitally reading or
// writing with them. Also, make sure the pin is in output mode
// for consistenty with Wiring, which doesn't require a pinMode
// call for the analog output pins.
pinMode(pin, OUTPUT);
if (val == 0)
{
digitalWrite(pin, LOW);
}
else if (val == 255)
{
digitalWrite(pin, HIGH);
}
在设置255时,analogWrite直接忽略PWM和输出比较这些操作,直接设置HIGH :<。
解决这个问题避开11和3就好了,所以这里将PWN管脚改成了PIN10。