Adafruit Grand Central M4 (ATSAMD51P20) - 22 PWM channels

Hi,

I need to use all 22 PWM channels on the board fr my project, and am using a variation of the standard fade example code to check it (below). I’m not sure how to go about configuring the pins to be PWM though.

According to Adafruit any pins from the below can be used:

A1, A2, A12, A15.
D2-D9, D11, D13-D45, D48, D50-D53.
MISO, MOSI, SCK, SCL, SDA.

So I am trying to use pins in the range of 22-43, and just using the analogueWrite command.
I get PWM signal on some but nothing on pins 22, 29,32,33,36,37,40,41,42,43.

I tried to look at the datasheet for help, but the nearly 2k was a bit overwhelming for a noob!

Any pointers would provide a great help in understanding this. I’m entirely new to the board and these chips.

/*
 Fade

 
 */
// designate PWM pins
int PWM1 = 22;          
int PWM2 = 23;
int PWM3 = 24; 
int PWM4 = 25; 
int PWM5 = 26; 
int PWM6 = 27; 
int PWM7 = 28; 
int PWM8 = 29; 
int PWM9 = 30; 
int PWM10 = 31; 
int PWM11 = 32; 
int PWM12 = 33; 
int PWM13 = 34; 
int PWM14 = 35; 
int PWM15 = 36; 
int PWM16 = 37; 
int PWM17 = 38;
int PWM18 = 39; 
int PWM19 = 40;
int PWM20 = 41; 
int PWM21 = 42; 
int PWM22 = 43;   
          
int brightness = 0;    // how bright the LED is
int fadeAmount = 5;    // how many points to fade the LED by

// the setup routine runs once when you press reset:
void setup() {
  // declare all PWM pins to be output:
  pinMode(PWM1, OUTPUT);
  pinMode(PWM2, OUTPUT);
  pinMode(PWM3, OUTPUT);
  pinMode(PWM4, OUTPUT);
  pinMode(PWM5, OUTPUT);
  pinMode(PWM6, OUTPUT);
  pinMode(PWM9, OUTPUT);
  pinMode(PWM10, OUTPUT);
  pinMode(PWM11, OUTPUT);
  pinMode(PWM12, OUTPUT);
  pinMode(PWM13, OUTPUT);
  pinMode(PWM14, OUTPUT);
  pinMode(PWM15, OUTPUT);
  pinMode(PWM16, OUTPUT);
  pinMode(PWM17, OUTPUT);
  pinMode(PWM18, OUTPUT);
  pinMode(PWM19, OUTPUT);
  pinMode(PWM20, OUTPUT);
  pinMode(PWM21, OUTPUT);
  pinMode(PWM22, OUTPUT);
  
}

// the loop routine runs over and over again forever:
void loop() {
  // set the brightness
  analogWrite(PWM1, brightness);
  analogWrite(PWM2, brightness);
  analogWrite(PWM3, brightness);
  analogWrite(PWM4, brightness);
  analogWrite(PWM5, brightness);
  analogWrite(PWM6, brightness);
  analogWrite(PWM7, brightness);
  analogWrite(PWM8, brightness);
  analogWrite(PWM9, brightness);
  analogWrite(PWM10, brightness);
  analogWrite(PWM11, brightness);
  analogWrite(PWM12, brightness);
  analogWrite(PWM13, brightness);
  analogWrite(PWM14, brightness);
  analogWrite(PWM15, brightness);
  analogWrite(PWM16, brightness);
  analogWrite(PWM17, brightness);
  analogWrite(PWM18, brightness);
  analogWrite(PWM19, brightness);
  analogWrite(PWM20, brightness);
  analogWrite(PWM21, brightness);
  analogWrite(PWM22, brightness);

  // change the brightness for next time through the loop:
  brightness = brightness + fadeAmount;

  // reverse the direction of the fading at the ends of the fade:
  if (brightness <= 0 || brightness >= 255) {
    fadeAmount = -fadeAmount;
  }
  // wait for 30 milliseconds to see the dimming effect
  delay(30);
}

The thing with libraries is they don't necessarily exposed all the capabilities of the underlying hardware.

The M4 has like 13 timers (which are used to generate PWM signals) with multiple compare channels. My guess is the library probably is just using two/maybe 3 out of the 13 available timers and the pins that are working are "routed" through these timers. Unfortunately, there is no easy way to "activate" the other PWM output since it would require initialization of the underlying clocks and timers.

The good news is, there's 8 TC timers, which in Cortex M speak, is pretty "basic" or straightforward to initialize. With two compare channels for each TC timer, you get 16 PWM outputs.

To get the other 6 PWM outputs, you would need two of the TCC timers but these are "not-very-simple-at-all" to work with, even for an experienced Cortex programmer.

Maybe someone with enough time in their hands and a Cortex M4 board like you have can gin up the necessary "low-level" code to make this happen.

llofyorkshire:
I tried to look at the datasheet for help, but the nearly 2k was a bit overwhelming for a noob!

Before you continue you should go back to the datasheet and try to find out how much current you can sink and source

  • per pin
  • per port
  • the entire package aka the power pins.

There should be a section in the datasheet called "Electrical characteristics" with tables and all kinds of numbers. If you need help, please post a link to the datasheet from the manufacturer web page. Ensures we look at the same document and you looked in the right place. :slight_smile:

How much current do you need at each PWM pin?

You need to see whether you can ensure that you will not exceed these values, even if something goes wrong and you enable all pins at the same time. Because that will happen while you write your software.

In theory, the Adafruit code supports both TC and TCC timers.
But a problem with the SAMD51 chip in general is that while “many pins support X”, that doesn’t mean that “all pins support X” at the same time.

For example, Pin22 is PD12, which is on “Channel 5” of TCC0. But TCC0/Chan5 is also the timer for digital pin 5 (PC21), so the Adafruit Arduino core enables the Pin5 PWM use, and not pin 22.

You can look at variants/grand_central_m4/variant.cpp to see which pins have NOT_ON_PWM,
or try this little sketch…

char buffer[80];
void setup() {
  Serial.begin(9600);
  while (!Serial)
    ;
  for (unsigned int i = 0; i < NUM_DIGITAL_PINS; i++) {
    if (g_APinDescription[i].ulPWMChannel != NOT_ON_PWM) {
      sprintf(buffer, "Pin #%d has PWM Channel 0x%03x\n", i, g_APinDescription[i].ulPWMChannel);
      Serial.print(buffer);
    }
  }
}
void loop() {}

Spoiler: it looks like 26 pins have PWM enabled. I don’t know if that’s the maximum possible.

Thanks all, I’ll give those suggestions a go and see where I get to.

and thanks for the tip Klaus. I’ll check the current draw requirements.

Perfect westfw! Thanks - that gave me all of the pins I an use and they are fine to use with just the analogueWrite command - no need for me to get into messing about with the low level code, which is great because I was using an ESP32 before for this project and needed to get right into the weeds with it (for 11PWMs at the time)

westfw:
In theory, the Adafruit code supports both TC and TCC timers.
But a problem with the SAMD51 chip in general is that while “many pins support X”, that doesn’t mean that “all pins support X” at the same time.

For example, Pin22 is PD12, which is on “Channel 5” of TCC0. But TCC0/Chan5 is also the timer for digital pin 5 (PC21), so the Adafruit Arduino core enables the Pin5 PWM use, and not pin 22.

You can look at variants/grand_central_m4/variant.cpp to see which pins have NOT_ON_PWM,
or try this little sketch…

char buffer[80];

void setup() {
  Serial.begin(9600);
  while (!Serial)
    ;
  for (unsigned int i = 0; i < NUM_DIGITAL_PINS; i++) {
    if (g_APinDescription[i].ulPWMChannel != NOT_ON_PWM) {
      sprintf(buffer, “Pin #%d has PWM Channel 0x%03x\n”, i, g_APinDescription[i].ulPWMChannel);
      Serial.print(buffer);
    }
  }
}
void loop() {}



Spoiler: it looks like 26 pins have PWM enabled. I don't know if that's the maximum possible.