PWM, Servo, and SPI on one Nano

I've got an upcoming project that will require servos, PWM, and SPI on an Arduino Nano:

  • four servo outputs (via Servo library)
  • four PWM outputs (via AnalogWrite for 5v laser diodes)
  • SPI (for Ethernet via ENC28J60 module and EtherCard library)

The Arduino page for AnalogWrite says that digital pins 9 and 10 cannot be used for PWM when the Servo library is in use. This leaves pins 3, 5, 6, and 11 for PWM. In my case, however, pin 11 is already taken by the SPI interface, leaving me short one PWM output.

Is there a way to get the Servo library to disable a different pair of PWM pins instead, such as pins 3 and 11? I suspect it has something to do with the internal oscillators (timers, right?) for the ATMega microcontroller. The pinout suggests that:

Digital pin 3 = OSC2B
Digital pin 5 = OSC0B
Digital pin 6 = OSC0A
Digital pin 9 = OSC1A
Digital pin 10 = OSC1B
Digital pin 11 = OSC2A / SPI MOSI

The pairings make sense, so I'm assuming there's a connection here with the Arduino timer functions, but this is diving just a little deeper into the internal workings of the ATMega chip than my experience has gone before. I poked around a little in the Servo library and found that it has a "#define _useTimer1" that seems relevant to the Nano's ATMega328P controller.

Is Timer1 the same as (or at least associated with) OSC1? If so, can the Servo library use a different timer, say OSC2? Are there pros/cons to each timer?

Worst-case scenario, I'll look into using something like a PCA9685 PWM controller via I2C, but I was hoping not to since the Nano already has enough output pins and space is at a premium.

I'll keep doing research and experimentation, but if someone would like to offer a nudge in the right direction, it would be appreciated!

Timer2 uses pins 11 and 3 so still a conflict with SPI. Maybe consider a SoftPWM library that can use any pins.

OK, so utilizing the timer causes the pins to be completely disabled? I was hoping that it only affected PWM functionality and that it wouldn't take out hardware SPI as well (which is exactly why I suggested Timer2).

I will look into the software PWM solution and see if it can run stable while Ethernet and Servo code is also in use (the idea is that the device will receive control data via Ethernet and set the servo positions and PWM outputs accordingly).

System integration is difficult.

@gF Thanks for the link. I downloaded that library. It feels like useful some day.

@gF again

Reading the .h files I get the opinion that the frequency is quite low. 30 Hz was mentioned. Can You tell some more? Can the freqency be altered, set....?

Railroader:
@gF again

Reading the .h files I get the opinion that the frequency is quite low. 30 Hz was mentioned. Can You tell some more? Can the freqency be altered, set....?

It's a hardcoded 60Hz in SoftPWM.cpp:

#if F_CPU
#define SOFTPWM_FREQ 60UL
#define SOFTPWM_OCR (F_CPU/(8UL*256UL*SOFTPWM_FREQ))
#else
// 130 == 60 Hz (on 16 MHz part)
#define SOFTPWM_OCR 130
#endif

Technically you could change it if you alter the .cpp, but whether or not that would even work might require some basic testing.

@P B.
Thanks. I didn't open the .cpp fileā€¦ 60Hz can be used in a variety of projects. Altering 60 to lower values ought to work. Just test it....

@groundFungus
Thanks for the SoftPWM suggestion! Tests so far seem to be promising.

Here's what I discovered thus far, in case anyone else happens to stumble upon this post.

Testing PWM functionality alone:
All six pins work fine (big surprise, but I had to start somewhere). Test was done with simple LEDs attached to each pin through a 220-470 ohm resistor.
Digital pin 3 = PWM LED 1
Digital pin 5 = PWM LED 2
Digital pin 6 = PWM LED 3
Digital pin 9 = PWM LED 4
Digital pin 10 = PWM LED 5
Digital pin 11 = PWM LED 6

Testing PWM + Servo library:
Pins 9 and 10 stopped working properly for PWM, and servo control didn't work until I stopped trying to use analogWrite on 9 and 10. The remaining PWM pins worked happily alongside a servo attached to digital pin 2. Only tested with one servo.
Digital pin 2 = servo 1
Digital pin 3 = PWM LED 1
Digital pin 5 = PWM LED 2
Digital pin 6 = PWM LED 3
Digital pin 9 = PWM disabled
Digital pin 10 = PWM disabled
Digital pin 11 = PWM LED 4

Testing PWM + Servo library + EtherCard library:
I used digital pin 8 for the ENC28J60 CS pin (it's the only pin that is configurable). Pins 11, 12, and 13 were used for the SI, SO, and SLK signals for the SPI interface. PWM on remaining pins (3, 5, 6) still worked. Only tested with one servo.
Digital pin 2 = servo 1
Digital pin 3 = PWM LED 1
Digital pin 5 = PWM LED 2
Digital pin 6 = PWM LED 3
Digital pin 8 = SPI CS
Digital pin 9 = PWM disabled due to Timer1
Digital pin 10 = PWM disabled due to Timer1
Digital pin 11 = SPI MOSI
Digital pin 12 = SPI MISO
Digital pin 13 = SPI SCK

Testing PWM + ServoTimer2 library + EtherCard library:
The ServoTimer2 library is an alternate library written to utilize Timer2 instead of Timer1. It supports fewer servos and lower accuracy, but it did work. This let me keep hardware PWM on pins 5, 6, 9, and 10, while sacrificing pins 3 and 11. SPI continued to work even though it uses pin 11. Only tested with one servo on pin 2.
Digital pin 2 = servo 1
Digital pin 3 = PWM disabled due to Timer2
Digital pin 5 = PWM LED 1
Digital pin 6 = PWM LED 2
Digital pin 8 = SPI CS
Digital pin 9 = PWM 3
Digital pin 10 = PWM 4
Digital pin 11 = SPI MOSI
Digital pin 12 = SPI MISO
Digital pin 13 = SPI SCK

Testing SoftPWM library + Servo library + EtherCard library:
Switching to the SoftPWM library also worked. At this point I can pretty much use any pin for anything (other than the ones dedicated to the SPI bus). Servos are working on pins 2, 3, 4, and 5. PWM LEDs are working on 6, 7, 8, and 9. SPI is on pins 10, 11, 12, and 13.
Digital pin 2 = servo1
Digital pin 3 = servo2
Digital pin 4 = servo3
Digital pin 5 = servo4
Digital pin 6 = SoftPWM LED 1
Digital pin 7 = SoftPWM LED 2
Digital pin 8 = SoftPWM LED 3
Digital pin 9 = SoftPWM LED 4
Digital pin 10 = SPI CS
Digital pin 11 = SPI MOSI
Digital pin 12 = SPI MISO
Digital pin 13 = SPI SCK

I haven't seen an obvious difference in performance between the last two scenarios, i.e. is the higher resolution of the standard Servo library preferred over the ServoTimer2 library, or is a faster PWM frequency via the hardware PWM better than the SoftPWM library. Time will tell once I get closer to a fully-built prototype. I'm using some pretty cheap SG90 servos anyway, so it's hard to know if the servo resolution issue is really going to be noticeable either way.

Thanks again for your help!