T4 Pin Drive Strength

J_Sanders

Active member
Hi all,

I'm hoping to learn whether someone has already developed a function to set the drive strength, speed and other configurable parameters of Teensy digital pins (esp. T4.0 and 4.1)

Something like setDriveStrength(Pin, Level)

If not, is there a thread or resource explaining how to do it with lower level code, e.g.
IOMUXC_SW_PAD_CTL_PAD_GPIO_AD_B1_10 = 0x8;

I've found snippets scattered on various threads, but no central source of wisdom.

Thanks!
-J
 
Would this work for drive-strength?

In file :: ...\hardware\teensy\avr\cores\teensy4\digital.c :: void pinMode(uint8_t pin, uint8_t mode)


void pinMode(uint8_t pin, uint8_t mode)
{
const struct digital_pin_bitband_and_config_table_struct *p;

if (pin >= CORE_NUM_DIGITAL) return;
p = digital_pin_to_info_PGM + pin;

if (mode == OUTPUT || mode == OUTPUT_OPENDRAIN)
{
*(p->reg + 1) |= p->mask; // TODO: atomic
if (mode == OUTPUT) // Default
{
*(p->pad) = IOMUXC_PAD_DSE(7);
}
else if (mode == OUTPUT_1) // 400 ohm
{
*(p->pad) = IOMUXC_PAD_DSE(1);
}
else if (mode == OUTPUT_2) // 200 ohm
{
*(p->pad) = IOMUXC_PAD_DSE(2);
}
else if (mode == OUTPUT_3) // 132 ohm
{
*(p->pad) = IOMUXC_PAD_DSE(3);
}
else if (mode == OUTPUT_4) // 104 ohm
{
*(p->pad) = IOMUXC_PAD_DSE(4);
}
else if (mode == OUTPUT_5) // 83 ohm
{
*(p->pad) = IOMUXC_PAD_DSE(5);
}
else if (mode == OUTPUT_6) // 70 ohm
{
*(p->pad) = IOMUXC_PAD_DSE(6);
}
else if (mode == OUTPUT_7) // 55 ohm - same as Default
{
*(p->pad) = IOMUXC_PAD_DSE(7);
}
else
{ // OUTPUT_OPENDRAIN
*(p->pad) = IOMUXC_PAD_DSE(7) | IOMUXC_PAD_ODE;
}
}
else
{
*(p->reg + 1) &= ~(p->mask); // TODO: atomic
if (mode == INPUT)
{
*(p->pad) = IOMUXC_PAD_DSE(7);
}
else if (mode == INPUT_PULLUP)
{
*(p->pad) = IOMUXC_PAD_DSE(7) | IOMUXC_PAD_PKE | IOMUXC_PAD_PUE | IOMUXC_PAD_PUS(3) | IOMUXC_PAD_HYS;
}
else if (mode == INPUT_PULLDOWN)
{
*(p->pad) = IOMUXC_PAD_DSE(7) | IOMUXC_PAD_PKE | IOMUXC_PAD_PUE | IOMUXC_PAD_PUS(0) | IOMUXC_PAD_HYS;
}
else
{ // INPUT_DISABLE
*(p->pad) = IOMUXC_PAD_DSE(7) | IOMUXC_PAD_HYS;
}
}
*(p->mux) = 5 | 0x10;
}
 
And update core_pins.h to add the underscore OUTPUT_Xs

...

#define HIGH 1
#define LOW 0
#define INPUT 0
#define OUTPUT 1
#define OUTPUT_1 1
#define OUTPUT_2 1
#define OUTPUT_3 1
#define OUTPUT_4 1
#define OUTPUT_5 1
#define OUTPUT_6 1
#define OUTPUT_7 1
#define INPUT_PULLUP 2
#define INPUT_PULLDOWN 3
#define OUTPUT_OPENDRAIN 4
#define INPUT_DISABLE 5
#define LSBFIRST 0
#define MSBFIRST 1
#define _BV(n) (1<<(n))
#define CHANGE 4
#define FALLING 2
#define RISING 3

...

Which comples - now need to test to see if the output impedance does actually change.
 
Thanks tonton81 - need to put brain into gear

Clunky, but it now works... with an updated .h file which I will post next

void pinMode(uint8_t pin, uint8_t mode)
{
const struct digital_pin_bitband_and_config_table_struct *p;

if (pin >= CORE_NUM_DIGITAL) return;
p = digital_pin_to_info_PGM + pin;

if (mode == OUTPUT_OPENDRAIN)
{
*(p->reg + 1) |= p->mask; // TODO: atomic
*(p->pad) = IOMUXC_PAD_DSE(7) | IOMUXC_PAD_ODE; // OUTPUT_OPENDRAIN
}
else if (mode == OUTPUT)
{
*(p->reg + 1) |= p->mask;
*(p->pad) = IOMUXC_PAD_DSE(7);
}
else if (mode == OUTPUT_1) // 400 ohm
{
*(p->reg + 1) |= p->mask;
*(p->pad) = IOMUXC_PAD_DSE(1);
}
else if (mode == OUTPUT_2) // 200 ohm
{
*(p->reg + 1) |= p->mask;
*(p->pad) = IOMUXC_PAD_DSE(2);
}
else if (mode == OUTPUT_3) // 132 ohm
{
*(p->reg + 1) |= p->mask;
*(p->pad) = IOMUXC_PAD_DSE(3);
}
else if (mode == OUTPUT_4) // 104 ohm
{
*(p->reg + 1) |= p->mask;
*(p->pad) = IOMUXC_PAD_DSE(4);
}
else if (mode == OUTPUT_5) // 83 ohm
{
*(p->reg + 1) |= p->mask;
*(p->pad) = IOMUXC_PAD_DSE(5);
}
else if (mode == OUTPUT_6) // 70 ohm
{
*(p->reg + 1) |= p->mask;
*(p->pad) = IOMUXC_PAD_DSE(6);
}
else if (mode == OUTPUT_7) // 55 ohm - same as Default
{
*(p->reg + 1) |= p->mask;
*(p->pad) = IOMUXC_PAD_DSE(7);
}
else
{
*(p->reg + 1) &= ~(p->mask); // TODO: atomic
if (mode == INPUT)
{
*(p->pad) = IOMUXC_PAD_DSE(7);
}
else if (mode == INPUT_PULLUP)
{
*(p->pad) = IOMUXC_PAD_DSE(7) | IOMUXC_PAD_PKE | IOMUXC_PAD_PUE | IOMUXC_PAD_PUS(3) | IOMUXC_PAD_HYS;
}
else if (mode == INPUT_PULLDOWN)
{
*(p->pad) = IOMUXC_PAD_DSE(7) | IOMUXC_PAD_PKE | IOMUXC_PAD_PUE | IOMUXC_PAD_PUS(0) | IOMUXC_PAD_HYS;
}
else
{ // INPUT_DISABLE
*(p->pad) = IOMUXC_PAD_DSE(7) | IOMUXC_PAD_HYS;
}
}
*(p->mux) = 5 | 0x10;
}
 
Updated core_pins.h

#define HIGH 1
#define LOW 0
#define INPUT 0
#define OUTPUT 1
#define OUTPUT_1 11
#define OUTPUT_2 12
#define OUTPUT_3 13
#define OUTPUT_4 14
#define OUTPUT_5 15
#define OUTPUT_6 16
#define OUTPUT_7 17
#define INPUT_PULLUP 2
#define INPUT_PULLDOWN 3
#define OUTPUT_OPENDRAIN 4
#define INPUT_DISABLE 5
#define LSBFIRST 0
#define MSBFIRST 1
#define _BV(n) (1<<(n))
#define CHANGE 4
#define FALLING 2
#define RISING 3
 
I tried it just now on a Teensy 4.0. The code needs a minor edit, since the first if condition doesn't allow OUTPUT_1 to OUTPUT_7. To be used within a normal program, the function needs a different name so it doesn't conflict with the original pinMode().

I tested by connecting a 464 ohm resistor to GND and measuring the voltage for logic high. VCC was measured at 3.286. The drive strength does indeed change, though the impedance is lower (stronger drive) than NXP's documentation.

Code:
           Ohms              Ohms
Setting   Claimed  Voltage  Actual

OUTPUT_1    400     2.588    125
OUTPUT_2    200     2.915     59
OUTPUT_4    104     3.107     27
OUTPUT_7     55     3.177     16

I tested only 1 board and only at room temperature. NXP's specs might be the worst case for all chips at max temperature?

Here's the code in the form of a complete working program. To recreate this measurement, just connect a 470 ohm resistor across a voltmeter and measure each pin's actual voltage.

Code:
#define HIGH 1
#define LOW 0
#define INPUT 0
#define OUTPUT 1
#define OUTPUT_1 11
#define OUTPUT_2 12
#define OUTPUT_3 13
#define OUTPUT_4 14
#define OUTPUT_5 15
#define OUTPUT_6 16
#define OUTPUT_7 17
#define INPUT_PULLUP 2
#define INPUT_PULLDOWN 3
#define OUTPUT_OPENDRAIN 4
#define INPUT_DISABLE 5
#define LSBFIRST 0
#define MSBFIRST 1
#define _BV(n) (1<<(n))
#define CHANGE 4
#define FALLING 2
#define RISING 3

void setup() {
  pinModeExt(2, OUTPUT_1);
  pinModeExt(3, OUTPUT_2);
  pinModeExt(4, OUTPUT_4);
  pinModeExt(5, OUTPUT_7);
  digitalWrite(2, HIGH);
  digitalWrite(3, HIGH);
  digitalWrite(4, HIGH);
  digitalWrite(5, HIGH);
}

void loop() {
  // put your main code here, to run repeatedly:

}

void pinModeExt(uint8_t pin, uint8_t mode)
{
  const struct digital_pin_bitband_and_config_table_struct *p;

  if (pin >= CORE_NUM_DIGITAL) return;
  p = digital_pin_to_info_PGM + pin;

  if (mode == OUTPUT || mode == OUTPUT_OPENDRAIN || (mode >= OUTPUT_1 && mode <= OUTPUT_7))
  {
    *(p->reg + 1) |= p->mask; // TODO: atomic
    if (mode == OUTPUT) // Default
    {
      *(p->pad) = IOMUXC_PAD_DSE(7);
    }
    else if (mode == OUTPUT_1) // 400 ohm
    {
      *(p->pad) = IOMUXC_PAD_DSE(1);
    }
    else if (mode == OUTPUT_2) // 200 ohm
    {
      *(p->pad) = IOMUXC_PAD_DSE(2);
    }
    else if (mode == OUTPUT_3) // 132 ohm
    {
      *(p->pad) = IOMUXC_PAD_DSE(3);
    }
    else if (mode == OUTPUT_4) // 104 ohm
    {
      *(p->pad) = IOMUXC_PAD_DSE(4);
    }
    else if (mode == OUTPUT_5) // 83 ohm
    {
      *(p->pad) = IOMUXC_PAD_DSE(5);
    }
    else if (mode == OUTPUT_6) // 70 ohm
    {
      *(p->pad) = IOMUXC_PAD_DSE(6);
    }
    else if (mode == OUTPUT_7) // 55 ohm - same as Default
    {
      *(p->pad) = IOMUXC_PAD_DSE(7);
    }
    else
    { // OUTPUT_OPENDRAIN
      *(p->pad) = IOMUXC_PAD_DSE(7) | IOMUXC_PAD_ODE;
    }
  }
  else
  {
    *(p->reg + 1) &= ~(p->mask); // TODO: atomic
    if (mode == INPUT)
    {
      *(p->pad) = IOMUXC_PAD_DSE(7);
    }
    else if (mode == INPUT_PULLUP)
    {
      *(p->pad) = IOMUXC_PAD_DSE(7) | IOMUXC_PAD_PKE | IOMUXC_PAD_PUE | IOMUXC_PAD_PUS(3) | IOMUXC_PAD_HYS;
    }
    else if (mode == INPUT_PULLDOWN)
    {
      *(p->pad) = IOMUXC_PAD_DSE(7) | IOMUXC_PAD_PKE | IOMUXC_PAD_PUE | IOMUXC_PAD_PUS(0) | IOMUXC_PAD_HYS;
    }
    else
    { // INPUT_DISABLE
      *(p->pad) = IOMUXC_PAD_DSE(7) | IOMUXC_PAD_HYS;
    }
  }
  *(p->mux) = 5 | 0x10;
}
 
Those 400, 200, 104, and 55 ohms seem to be for 1.8V setup, for the Rpu only?...
Corresponding values for 3.3V nominal are 220, 110, 58 and 32... Still high..
However, if looking at the "Data Sheet: Technical data" (the small pdf..), page 38, the AC-impedances (couldn't figure out what was the frequency of measurement), the corresponding values could be:
157, 78, 39, and 23...
(Though I can't see how that would match with anything declared in the datasheets for DC.. And the table a bit above talks about Rpu at 22k, 47k, 100k.. That is one very ambiguous datasheet combination. Different numbers thrown around with related terms, either without explanation or given explanation not matching the other sets of data.)
 
Last edited:
Hi Paul,

Awesome, thanks for the update.

I tried it just now on a Teensy 4.0. The code needs a minor edit, since the first if condition doesn't allow OUTPUT_1 to OUTPUT_7. To be used within a normal program, the function needs a different name so it doesn't conflict with the original pinMode().
 
Alternately adding an extra parameter... although one has to specify something in the third position even for inputs

Code:
// See i.MX RT1060 Processor Reference Manual, Rev. 2, pages 385 to 399

void pinModeExt(uint8_t pin, uint8_t mode, uint8_t strength)
  {
  const struct digital_pin_bitband_and_config_table_struct *p;

  if (pin >= CORE_NUM_DIGITAL) return;
  p = digital_pin_to_info_PGM + pin;

  if(mode == OUTPUT || mode == OUTPUT_OPENDRAIN)
    {
    *(p->reg + 1) |= p->mask; // TODO: atomic
    if (mode == OUTPUT) // Default
      {
      switch(strength)
        {
        case 1:
          *(p->pad) = IOMUXC_PAD_DSE(1);
          break;
        case 2:
          *(p->pad) = IOMUXC_PAD_DSE(2);
          break;
        case 3:
          *(p->pad) = IOMUXC_PAD_DSE(3);
          break;
        case 4:
          *(p->pad) = IOMUXC_PAD_DSE(4);
          break;
        case 5:
          *(p->pad) = IOMUXC_PAD_DSE(5);
          break;
        case 6:
          *(p->pad) = IOMUXC_PAD_DSE(6);
          break;
        case 7:
          *(p->pad) = IOMUXC_PAD_DSE(7);
          break;
        default:
          *(p->pad) = IOMUXC_PAD_DSE(7);
          break;
        }
      }
    else
      { // OUTPUT_OPENDRAIN
      *(p->pad) = IOMUXC_PAD_DSE(7) | IOMUXC_PAD_ODE;
      }
    }
  else
    {
    *(p->reg + 1) &= ~(p->mask); // TODO: atomic
    if (mode == INPUT)
      {
      *(p->pad) = IOMUXC_PAD_DSE(7);
      }
    else if (mode == INPUT_PULLUP)
      {
      *(p->pad) = IOMUXC_PAD_DSE(7) | IOMUXC_PAD_PKE | IOMUXC_PAD_PUE | IOMUXC_PAD_PUS(3) | IOMUXC_PAD_HYS;
      }
    else if (mode == INPUT_PULLDOWN)
      {
      *(p->pad) = IOMUXC_PAD_DSE(7) | IOMUXC_PAD_PKE | IOMUXC_PAD_PUE | IOMUXC_PAD_PUS(0) | IOMUXC_PAD_HYS;
      }
    else
      { // INPUT_DISABLE
      *(p->pad) = IOMUXC_PAD_DSE(7) | IOMUXC_PAD_HYS;
      }
    }
  *(p->mux) = 5 | 0x10;
  }
 
Has anyone written a routine for the INPUT_PULLUP resistor options?


I tried it just now on a Teensy 4.0. The code needs a minor edit, since the first if condition doesn't allow OUTPUT_1 to OUTPUT_7. To be used within a normal program, the function needs a different name so it doesn't conflict with the original pinMode().

I tested by connecting a 464 ohm resistor to GND and measuring the voltage for logic high. VCC was measured at 3.286. The drive strength does indeed change, though the impedance is lower (stronger drive) than NXP's documentation.

Code:
           Ohms              Ohms
Setting   Claimed  Voltage  Actual

OUTPUT_1    400     2.588    125
OUTPUT_2    200     2.915     59
OUTPUT_4    104     3.107     27
OUTPUT_7     55     3.177     16

I tested only 1 board and only at room temperature. NXP's specs might be the worst case for all chips at max temperature?

Here's the code in the form of a complete working program. To recreate this measurement, just connect a 470 ohm resistor across a voltmeter and measure each pin's actual voltage.

Code:
#define HIGH 1
#define LOW 0
#define INPUT 0
#define OUTPUT 1
#define OUTPUT_1 11
#define OUTPUT_2 12
#define OUTPUT_3 13
#define OUTPUT_4 14
#define OUTPUT_5 15
#define OUTPUT_6 16
#define OUTPUT_7 17
#define INPUT_PULLUP 2
#define INPUT_PULLDOWN 3
#define OUTPUT_OPENDRAIN 4
#define INPUT_DISABLE 5
#define LSBFIRST 0
#define MSBFIRST 1
#define _BV(n) (1<<(n))
#define CHANGE 4
#define FALLING 2
#define RISING 3

void setup() {
  pinModeExt(2, OUTPUT_1);
  pinModeExt(3, OUTPUT_2);
  pinModeExt(4, OUTPUT_4);
  pinModeExt(5, OUTPUT_7);
  digitalWrite(2, HIGH);
  digitalWrite(3, HIGH);
  digitalWrite(4, HIGH);
  digitalWrite(5, HIGH);
}

void loop() {
  // put your main code here, to run repeatedly:

}

void pinModeExt(uint8_t pin, uint8_t mode)
{
  const struct digital_pin_bitband_and_config_table_struct *p;

  if (pin >= CORE_NUM_DIGITAL) return;
  p = digital_pin_to_info_PGM + pin;

  if (mode == OUTPUT || mode == OUTPUT_OPENDRAIN || (mode >= OUTPUT_1 && mode <= OUTPUT_7))
  {
    *(p->reg + 1) |= p->mask; // TODO: atomic
    if (mode == OUTPUT) // Default
    {
      *(p->pad) = IOMUXC_PAD_DSE(7);
    }
    else if (mode == OUTPUT_1) // 400 ohm
    {
      *(p->pad) = IOMUXC_PAD_DSE(1);
    }
    else if (mode == OUTPUT_2) // 200 ohm
    {
      *(p->pad) = IOMUXC_PAD_DSE(2);
    }
    else if (mode == OUTPUT_3) // 132 ohm
    {
      *(p->pad) = IOMUXC_PAD_DSE(3);
    }
    else if (mode == OUTPUT_4) // 104 ohm
    {
      *(p->pad) = IOMUXC_PAD_DSE(4);
    }
    else if (mode == OUTPUT_5) // 83 ohm
    {
      *(p->pad) = IOMUXC_PAD_DSE(5);
    }
    else if (mode == OUTPUT_6) // 70 ohm
    {
      *(p->pad) = IOMUXC_PAD_DSE(6);
    }
    else if (mode == OUTPUT_7) // 55 ohm - same as Default
    {
      *(p->pad) = IOMUXC_PAD_DSE(7);
    }
    else
    { // OUTPUT_OPENDRAIN
      *(p->pad) = IOMUXC_PAD_DSE(7) | IOMUXC_PAD_ODE;
    }
  }
  else
  {
    *(p->reg + 1) &= ~(p->mask); // TODO: atomic
    if (mode == INPUT)
    {
      *(p->pad) = IOMUXC_PAD_DSE(7);
    }
    else if (mode == INPUT_PULLUP)
    {
      *(p->pad) = IOMUXC_PAD_DSE(7) | IOMUXC_PAD_PKE | IOMUXC_PAD_PUE | IOMUXC_PAD_PUS(3) | IOMUXC_PAD_HYS;
    }
    else if (mode == INPUT_PULLDOWN)
    {
      *(p->pad) = IOMUXC_PAD_DSE(7) | IOMUXC_PAD_PKE | IOMUXC_PAD_PUE | IOMUXC_PAD_PUS(0) | IOMUXC_PAD_HYS;
    }
    else
    { // INPUT_DISABLE
      *(p->pad) = IOMUXC_PAD_DSE(7) | IOMUXC_PAD_HYS;
    }
  }
  *(p->mux) = 5 | 0x10;
}
 
Are the T3.2 outputs stronger than on T4.x’s? I am using some 470 pull-ups on i2c lines and my want to consider a higher value. Just used 470 because a had a strip of 100 handy.
 
Back
Top