PWM voltage on Mega 2560 analog pins 8-15 using arduino-softpwm library

Here is the original: github.com/Palatis/arduino-softpwm

Here is it now:

/*
  Arduino-SoftPWM: a software PWM library for Arduino
  Copyright 2016, Victor Tseng <palatis@gmail.com>
  All rights reserved.
*/

#include <SoftPWM.h>

SOFTPWM_DEFINE_CHANNEL(22, DDRB, PORTB, PORTB0);  //Arduino pin A8
SOFTPWM_DEFINE_CHANNEL(23, DDRB, PORTB, PORTB5);  //Arduino pin A9
SOFTPWM_DEFINE_CHANNEL(24, DDRC, PORTC, PORTC0);  //Arduino pin A10
SOFTPWM_DEFINE_CHANNEL(25, DDRC, PORTC, PORTC1);  //Arduino pin A11
SOFTPWM_DEFINE_CHANNEL(26, DDRC, PORTC, PORTC2);  //Arduino pin A12
SOFTPWM_DEFINE_CHANNEL(27, DDRC, PORTC, PORTC3);  //Arduino pin A13
SOFTPWM_DEFINE_CHANNEL(28, DDRC, PORTC, PORTC4);  //Arduino pin A14
SOFTPWM_DEFINE_CHANNEL(29, DDRC, PORTC, PORTC5);  //Arduino pin A15

/* Here you make an instance of desired channel counts you want
   with the default 256 PWM levels (0 ~ 255). */
//SOFTPWM_DEFINE_OBJECT(20);

/* Or you can make one with only 100 PWM levels (0 ~ 99).
   By using less PWM levels, you may be able to use higher
   pwm frequencies. */
SOFTPWM_DEFINE_OBJECT_WITH_PWM_LEVELS(20, 100);        

/* If you want to use the SoftPWM object outside where it's defined,
   add the following line to the file. */
//SOFTPWM_DEFINE_EXTERN_OBJECT(16);
SOFTPWM_DEFINE_EXTERN_OBJECT_WITH_PWM_LEVELS(20, 100);

void setup() {
  Serial.begin(19200);

  // begin with 60hz pwm frequency
  Palatis::SoftPWM.begin(60);

  // print interrupt load for diagnostic purposes
  Palatis::SoftPWM.printInterruptLoad();
}

void loop() {
  
      for(int pin = 0; pin < 8; pin++)
        for(int s = 63; s <= 255; s += 64)
          {          // PWM: 255 = 5V
            Palatis::SoftPWM.set(pin, s);     // sweeps 1.24, 2.5, 3.75 & 5 V
            delay(500);
          }
}

I am just trying to output a PWM voltage sweep on non-PWM pins (specifically A8-A15).

What do I need to look into or what should I change?

adamaero:
SOFTPWM_DEFINE_CHANNEL(22, DDRB, PORTB, PORTB0); //Arduino pin A8
SOFTPWM_DEFINE_CHANNEL(23, DDRB, PORTB, PORTB5); //Arduino pin A9
SOFTPWM_DEFINE_CHANNEL(24, DDRC, PORTC, PORTC0); //Arduino pin A10
SOFTPWM_DEFINE_CHANNEL(25, DDRC, PORTC, PORTC1); //Arduino pin A11
SOFTPWM_DEFINE_CHANNEL(26, DDRC, PORTC, PORTC2); //Arduino pin A12
SOFTPWM_DEFINE_CHANNEL(27, DDRC, PORTC, PORTC3); //Arduino pin A13
SOFTPWM_DEFINE_CHANNEL(28, DDRC, PORTC, PORTC4); //Arduino pin A14
SOFTPWM_DEFINE_CHANNEL(29, DDRC, PORTC, PORTC5); //Arduino pin A15

Those are the pin mappings for the ATmega328P family. You need to change them to pin mappings for the ATmega2560:

I also have a fork of the library that allows you to define channels using Arduino pin numbers instead of needing to look up the correct port/bit mapping:

How do you define channels using Arduino pin numbers?

Is it in the example sketch or here:
github.com/per1234/PalatisSoftPWM/blob/master/PalatisSoftPWM.h#L194

The PalatisSoftPWM_example does demonstrate it. You can also see the implementation in the .h file. Here's how it would be done in your case:

SOFTPWM_DEFINE_PINA8_CHANNEL(0);  //Configure Arduino pin A8 as PWM channel 0
SOFTPWM_DEFINE_PINA9_CHANNEL(1);
SOFTPWM_DEFINE_PINA10_CHANNEL(2);
SOFTPWM_DEFINE_PINA11_CHANNEL(3);
SOFTPWM_DEFINE_PINA12_CHANNEL(4);
SOFTPWM_DEFINE_PINA13_CHANNEL(5);
SOFTPWM_DEFINE_PINA14_CHANNEL(6);
SOFTPWM_DEFINE_PINA15_CHANNEL(7);

In the end, it works just the same as the arduino-softpwm library, but this is much more friendly to Arduino users, who are often not familiar with how the port/bit pin mappings work.

So I changed SOFTPWM_DEFINE_OBJECT to 8 (instead of 1)

#include <PalatisSoftPWM.h>

SOFTPWM_DEFINE_PINA8_CHANNEL(0);  //Configure Arduino pin A8 as PWM channel 0
SOFTPWM_DEFINE_PINA9_CHANNEL(1);
SOFTPWM_DEFINE_PINA10_CHANNEL(2);
SOFTPWM_DEFINE_PINA11_CHANNEL(3);
SOFTPWM_DEFINE_PINA12_CHANNEL(4);
SOFTPWM_DEFINE_PINA13_CHANNEL(5);
SOFTPWM_DEFINE_PINA14_CHANNEL(6);
SOFTPWM_DEFINE_PINA15_CHANNEL(7);

SOFTPWM_DEFINE_OBJECT(8);

void setup() {
  PalatisSoftPWM.begin(60);  // begin with 60 Hz PWM frequency
}

void loop() {
   for(int pin = 0; pin < 8; pin++)
        for(int s = 63; s <= 255; s += 64)
          {          // PWM: 255 = 5V
            PalatisSoftPWM.set(pin, s);     // sweeps 1.24, 2.5, 3.75 & 5 V
            delay(500);
          }
}

I get these errors with defining the channels:

Arduino: 1.8.5 (Windows 7), Board: "Arduino/Genuino Mega or Mega 2560, ATmega2560 (Mega 2560)"

In file included from sketch\PalatisSoftPWM_example.ino.cpp:1:0:

C:\Users\uraynara\Documents\Arduino\PalatisSoftPWM-master\examples\PalatisSoftPWM_example\PalatisSoftPWM_example.ino: In function 'void {anonymous}::pinModeStatic(uint8_t) [with int channel = 1; uint8_t = unsigned char]':

C:\Users\uraynara\Documents\Arduino\libraries\PalatisSoftPWM-master/PalatisSoftPWM.h:322:79: error: 'DDRK1' was not declared in this scope

 #define SOFTPWM_DEFINE_PIN63_CHANNEL(CHANNEL) SOFTPWM_DEFINE_CHANNEL(CHANNEL, DDRK1, PORTK, PORTK1)

                                                                               ^

C:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino/Arduino.h:113:32: note: in definition of macro 'bitClear'

 #define bitClear(value, bit) ((value) &= ~(1UL << (bit)))

                                ^

C:\Users\uraynara\Documents\Arduino\libraries\PalatisSoftPWM-master/PalatisSoftPWM.h:1215:3: note: in expansion of macro 'SOFTPWM_DEFINE_PINMODE'

   SOFTPWM_DEFINE_PINMODE( CHANNEL, PMODE, PORT, BIT )

   ^

C:\Users\uraynara\Documents\Arduino\libraries\PalatisSoftPWM-master/PalatisSoftPWM.h:322:47: note: in expansion of macro 'SOFTPWM_DEFINE_CHANNEL'

 #define SOFTPWM_DEFINE_PIN63_CHANNEL(CHANNEL) SOFTPWM_DEFINE_CHANNEL(CHANNEL, DDRK1, PORTK, PORTK1)

                                               ^

C:\Users\uraynara\Documents\Arduino\libraries\PalatisSoftPWM-master/PalatisSoftPWM.h:354:47: note: in expansion of macro 'SOFTPWM_DEFINE_PIN63_CHANNEL'

 #define SOFTPWM_DEFINE_PINA9_CHANNEL(CHANNEL) SOFTPWM_DEFINE_PIN63_CHANNEL(CHANNEL)

                                               ^

C:\Users\uraynara\Documents\Arduino\PalatisSoftPWM-master\examples\PalatisSoftPWM_example\PalatisSoftPWM_example.ino:4:1: note: in expansion of macro 'SOFTPWM_DEFINE_PINA9_CHANNEL'

 SOFTPWM_DEFINE_PINA9_CHANNEL(1);

 ^

C:\Users\uraynara\Documents\Arduino\libraries\PalatisSoftPWM-master/PalatisSoftPWM.h:322:79: error: 'DDRK1' was not declared in this scope

 #define SOFTPWM_DEFINE_PIN63_CHANNEL(CHANNEL) SOFTPWM_DEFINE_CHANNEL(CHANNEL, DDRK1, PORTK, PORTK1)

                                                                               ^

C:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino/Arduino.h:113:32: note: in definition of macro 'bitClear'

 #define bitClear(value, bit) ((value) &= ~(1UL << (bit)))

                                ^

C:\Users\uraynara\Documents\Arduino\libraries\PalatisSoftPWM-master/PalatisSoftPWM.h:1215:3: note: in expansion of macro 'SOFTPWM_DEFINE_PINMODE'

   SOFTPWM_DEFINE_PINMODE( CHANNEL, PMODE, PORT, BIT )

   ^

C:\Users\uraynara\Documents\Arduino\libraries\PalatisSoftPWM-master/PalatisSoftPWM.h:322:47: note: in expansion of macro 'SOFTPWM_DEFINE_CHANNEL'

 #define SOFTPWM_DEFINE_PIN63_CHANNEL(CHANNEL) SOFTPWM_DEFINE_CHANNEL(CHANNEL, DDRK1, PORTK, PORTK1)

                                               ^

C:\Users\uraynara\Documents\Arduino\libraries\PalatisSoftPWM-master/PalatisSoftPWM.h:354:47: note: in expansion of macro 'SOFTPWM_DEFINE_PIN63_CHANNEL'

 #define SOFTPWM_DEFINE_PINA9_CHANNEL(CHANNEL) SOFTPWM_DEFINE_PIN63_CHANNEL(CHANNEL)

                                               ^

C:\Users\uraynara\Documents\Arduino\PalatisSoftPWM-master\examples\PalatisSoftPWM_example\PalatisSoftPWM_example.ino:4:1: note: in expansion of macro 'SOFTPWM_DEFINE_PINA9_CHANNEL'

 SOFTPWM_DEFINE_PINA9_CHANNEL(1);

 ^

C:\Users\uraynara\Documents\Arduino\libraries\PalatisSoftPWM-master/PalatisSoftPWM.h:322:79: error: 'DDRK1' was not declared in this scope

 #define SOFTPWM_DEFINE_PIN63_CHANNEL(CHANNEL) SOFTPWM_DEFINE_CHANNEL(CHANNEL, DDRK1, PORTK, PORTK1)

                                                                               ^

C:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino/Arduino.h:112:30: note: in definition of macro 'bitSet'

 #define bitSet(value, bit) ((value) |= (1UL << (bit)))

                              ^

C:\Users\uraynara\Documents\Arduino\libraries\PalatisSoftPWM-master/PalatisSoftPWM.h:1215:3: note: in expansion of macro 'SOFTPWM_DEFINE_PINMODE'

   SOFTPWM_DEFINE_PINMODE( CHANNEL, PMODE, PORT, BIT )

   ^

C:\Users\uraynara\Documents\Arduino\libraries\PalatisSoftPWM-master/PalatisSoftPWM.h:322:47: note: in expansion of macro 'SOFTPWM_DEFINE_CHANNEL'

 #define SOFTPWM_DEFINE_PIN63_CHANNEL(CHANNEL) SOFTPWM_DEFINE_CHANNEL(CHANNEL, DDRK1, PORTK, PORTK1)

                                               ^

C:\Users\uraynara\Documents\Arduino\libraries\PalatisSoftPWM-master/PalatisSoftPWM.h:354:47: note: in expansion of macro 'SOFTPWM_DEFINE_PIN63_CHANNEL'

 #define SOFTPWM_DEFINE_PINA9_CHANNEL(CHANNEL) SOFTPWM_DEFINE_PIN63_CHANNEL(CHANNEL)

                                               ^

C:\Users\uraynara\Documents\Arduino\PalatisSoftPWM-master\examples\PalatisSoftPWM_example\PalatisSoftPWM_example.ino:4:1: note: in expansion of macro 'SOFTPWM_DEFINE_PINA9_CHANNEL'

 SOFTPWM_DEFINE_PINA9_CHANNEL(1);

 ^

exit status 1

Compiles with this:

SOFTPWM_DEFINE_PINA8_CHANNEL(0);  //Configure Arduino pin A8 as PWM channel 0
SOFTPWM_DEFINE_PIN23_CHANNEL(1);
SOFTPWM_DEFINE_PIN24_CHANNEL(2);
SOFTPWM_DEFINE_PIN25_CHANNEL(3);
SOFTPWM_DEFINE_PIN26_CHANNEL(4);
SOFTPWM_DEFINE_PIN27_CHANNEL(5);
SOFTPWM_DEFINE_PIN28_CHANNEL(6);
SOFTPWM_DEFINE_PIN29_CHANNEL(7);

I am just trying to output a PWM voltage sweep on non-PWM pins

I assume that you realise that a PWM output is either at 0V or 5V and simply switches between the two values

UKHeliBob:
I assume that you realise that a PWM output is either at 0V or 5V and simply switches between the two values

Yes. What does this have to do with the issue? Is it not 0-255 with this program?

void loop() {
PalatisSoftPWM.set(0, 255);
//PalatisSoftPWM.set(1, 255);
//PalatisSoftPWM.set(2, 255);
//PalatisSoftPWM.set(3, 255);
//PalatisSoftPWM.set(4, 255);
//PalatisSoftPWM.set(5, 255);
//PalatisSoftPWM.set(6, 255);
//PalatisSoftPWM.set(7, 255);
}

What does this have to do with the issue?

Nothing directly, but you will not get

a PWM voltage sweep

from an Arduino pin.

Is it not 0-255 with this program?

Nothing is 0-255 in that program snippet. Writing 255 to a PWM pin will output 5V

Thanks so much for reporting this bug adamaero! It was a typo in the register name in the SOFTPWM_DEFINE_PIN63_CHANNEL macro definition. It should have been DDRK instead of DDRK1. I have fixed this and am working on a new release of the library with the bug fixed right now.

If you want to patch your existing installation of the library, you only need to change line 322 of PalatisSoftPWM.h from:

#define SOFTPWM_DEFINE_PIN63_CHANNEL(CHANNEL) SOFTPWM_DEFINE_CHANNEL(CHANNEL, DDRK1, PORTK, PORTK1)
#define SOFTPWM_DEFINE_PIN63_CHANNEL(CHANNEL) SOFTPWM_DEFINE_CHANNEL(CHANNEL, DDRK, PORTK, PORTK1)

Otherwise, you can download the library from GitHub and replace your existing installation.

@pert Thanks.

@UKHeliBob Again, I understand PWM. In the original post, I wrote a simple voltage sweep in void loop().

Back to the current issue, why isn't a voltage being output:

#include <PalatisSoftPWM.h>

SOFTPWM_DEFINE_PINA8_CHANNEL(0);  //Configure Arduino pin A8 as PWM channel 0
//SOFTPWM_DEFINE_PINA9_CHANNEL(1);
//SOFTPWM_DEFINE_PINA10_CHANNEL(2);
//SOFTPWM_DEFINE_PINA11_CHANNEL(3);
//SOFTPWM_DEFINE_PINA12_CHANNEL(4);
//SOFTPWM_DEFINE_PINA13_CHANNEL(5);
//SOFTPWM_DEFINE_PINA14_CHANNEL(6);
//SOFTPWM_DEFINE_PINA15_CHANNEL(7);

SOFTPWM_DEFINE_OBJECT(8);

void setup() {
  PalatisSoftPWM.begin(60);  // begin with 60 Hz PWM frequency
}

void loop() {
PalatisSoftPWM.set(0, 255);
//PalatisSoftPWM.set(1, 255);
//PalatisSoftPWM.set(2, 255);
//PalatisSoftPWM.set(3, 255);
//PalatisSoftPWM.set(4, 255);
//PalatisSoftPWM.set(5, 255);
//PalatisSoftPWM.set(6, 255);
//PalatisSoftPWM.set(7, 255);
}

Please humour me, but how does

        for(int s = 63; s <= 255; s += 64)
          {          // PWM: 255 = 5V
            Palatis::SoftPWM.set(pin, s);     // sweeps 1.24, 2.5, 3.75 & 5 V
            delay(500);
          }

output anything other than 0V or 5V ?

On a regular PWM pin, the loop does a voltage sweep.

Since the soft PWM program is in bytes, it's not as easy.

On a regular PWM pin, the loop does a voltage sweep.

How are you measuring the voltage ?

UKHeliBob:
How are you measuring the voltage ?

DMM. Maybe the fade part of pert's program works like a sweep, if a delay is added...

adamaero:
DMM.

What will be reacting to the output voltage in the project, or are you just doing this for the sake of experimentation ?

UKHeliBob:
What will be reacting to the output voltage in the project, or are you just doing this for the sake of experimentation ?

It needs to be noticeable enough to be read on a DMM. The DMM won't be attached after. A datalogger (Flexs Q4) will be reading the voltage. If there's a way to see the voltage output in the serial monitor, I'd be happy with that too.

I re-downloaded the PalatisSoftPWM library. It worked yesterday for a time, but now now only A8 works (A9-A15 do not). Same error message which is odd because it worked yesterday after the bug fix!

#include <PalatisSoftPWM.h>

//SOFTPWM_DEFINE_PINA8_CHANNEL(0);  //Configure Arduino pin A8 as PWM channel 0
SOFTPWM_DEFINE_PINA9_CHANNEL(0);
//SOFTPWM_DEFINE_PINA10_CHANNEL(2);
//SOFTPWM_DEFINE_PINA11_CHANNEL(3);
//SOFTPWM_DEFINE_PINA12_CHANNEL(4);
//SOFTPWM_DEFINE_PINA13_CHANNEL(5);
//SOFTPWM_DEFINE_PINA14_CHANNEL(6);
//SOFTPWM_DEFINE_PINA15_CHANNEL(7);

SOFTPWM_DEFINE_OBJECT(1);

void setup() {
  PalatisSoftPWM.begin(60);  // begin with 60 Hz PWM frequency
}

void loop() {
//PalatisSoftPWM.set(0, 255);
PalatisSoftPWM.set(0, 255);
//PalatisSoftPWM.set(2, 255);
//PalatisSoftPWM.set(3, 255);
//PalatisSoftPWM.set(4, 255);
//PalatisSoftPWM.set(5, 255);
//PalatisSoftPWM.set(6, 255);
//PalatisSoftPWM.set(7, 255);
}

So I went back to the other arduino-softpwm library. I changed pin A9 to 88, I think that's right, from the 2560 pin map. But that didn't work either. I'm also getting about 30mV on inactive pins which is confusing. Here is the program with this second library:

#include <SoftPWM.h>
SOFTPWM_DEFINE_CHANNEL(88, DDRD, PORTD, PORTD0);  //Arduino pin 0

//SOFTPWM_DEFINE_OBJECT(1);

/* Or you can make one with only 100 PWM levels (0 ~ 99).
   By uasing less PWM levels, you may be able to use higher
   pwm frequencies. */
SOFTPWM_DEFINE_OBJECT_WITH_PWM_LEVELS(20, 100);

/* If you want to use the SoftPWM object outside where it's defined,
   add the following line to the file. */
//SOFTPWM_DEFINE_EXTERN_OBJECT(16);
SOFTPWM_DEFINE_EXTERN_OBJECT_WITH_PWM_LEVELS(20, 100);

void setup() {
  Palatis::SoftPWM.begin(60);
}

void loop() {
      Palatis::SoftPWM.set(0, 255);
}

From post #10, at least the voltage is being output at A8 now.

adamaero:
I re-downloaded the PalatisSoftPWM library. It worked yesterday for a time, but now now only A8 works (A9-A15 do not). Same error message which is odd because it worked yesterday after the bug fix!

I'm sorry to hear that. I just compiled your sketch with all the channels defined and I get no errors:

#include <PalatisSoftPWM.h>

SOFTPWM_DEFINE_PINA8_CHANNEL(0);  //Configure Arduino pin A8 as PWM channel 0
SOFTPWM_DEFINE_PINA9_CHANNEL(1);
SOFTPWM_DEFINE_PINA10_CHANNEL(2);
SOFTPWM_DEFINE_PINA11_CHANNEL(3);
SOFTPWM_DEFINE_PINA12_CHANNEL(4);
SOFTPWM_DEFINE_PINA13_CHANNEL(5);
SOFTPWM_DEFINE_PINA14_CHANNEL(6);
SOFTPWM_DEFINE_PINA15_CHANNEL(7);

SOFTPWM_DEFINE_OBJECT(8);

void setup() {
  PalatisSoftPWM.begin(60);  // begin with 60 Hz PWM frequency
}

void loop() {
  //PalatisSoftPWM.set(0, 255);
  PalatisSoftPWM.set(0, 255);
  //PalatisSoftPWM.set(2, 255);
  //PalatisSoftPWM.set(3, 255);
  //PalatisSoftPWM.set(4, 255);
  //PalatisSoftPWM.set(5, 255);
  //PalatisSoftPWM.set(6, 255);
  //PalatisSoftPWM.set(7, 255);
}

adamaero:
So I went back to the other arduino-softpwm library. I changed pin A9 to 88, I think that's right, from the 2560 pin map. But that didn't work either. I'm also getting about 30mV on inactive pins which is confusing. Here is the program with this second library:

#include <SoftPWM.h>

SOFTPWM_DEFINE_CHANNEL(88, DDRD, PORTD, PORTD0);  //Arduino pin 0

This is incorrect. You define channels by the pin's port and bit. If you look at the 2560 pin map, you'll notice it says "PK1" next to the A9 pin. That means it's port K, bit 1. The "88" is just the physical pin number, which is not used for anything. What your code above does is to define PWM channel 88 (first parameter is channel number) as PD0. PD0 is actually Arduino pin 21. If you wanted to define PWM channel 0 as Arduino pin A9, the code would look like this:

SOFTPWM_DEFINE_CHANNEL(0, DDRK, PORTK, PORTK1);  //Arduino pin A9

Since most Arduino users have never been exposed to this low level port/bit pin notation system, the arduino-softpwm library is not very beginner friendly. That is the reason why I created my fork. I have had discussion with the author of the original library about merging my work into their library but I never managed to make this happen.