Strange digitalWrite behavior on Arduino Due

Hi All,

I actually created some work with an Arduino Due. I try to read external 16 bit AD converters which require a CS, conversion signal, SPI clock and MISO.
The CS here is a bit special, because I need tons of them I generate them with digitalWrite.
The plot attached shows on CH1 how the CS looks like (good).
The conversion signal shall be set low by digitalWrite(41, LOW) but the plot CH2 shows that it does not! Instead it decays very slowly!

By the way it does not matter what I connect to it. I can disconnect it, put a 1k resistor to GND or whatever, it always looks like this.

I then tried as well to do the same independently from the SPI communication on another pin (8). When I use for example ShiftOut and put the data line to 8, then it works fine, but if I use digitalWrite alone in the 100ms loop then it decays slowly again.

I do not really have an explanation to this, but what I do not really understand is what the firmware does:

extern void digitalWrite( uint32_t ulPin, uint32_t ulVal )
{
  /* Handle */
	if ( g_APinDescription[ulPin].ulPinType == PIO_NOT_A_PIN )
  {
    return ;
  }

  if ( PIO_GetOutputDataStatus( g_APinDescription[ulPin].pPort, g_APinDescription[ulPin].ulPin ) == 0 )
  {
    [b]PIO_PullUp( g_APinDescription[ulPin].pPort, g_APinDescription[ulPin].ulPin, ulVal ) ;[/b]
  }
  else
  {
   [b] PIO_SetOutput( g_APinDescription[ulPin].pPort, g_APinDescription[ulPin].ulPin, ulVal, 0, PIO_PULLUP [/b]) ;
  }
}

Once it uses PIO_PullUp and once PIO_SetOutput and I cannot figure out why and when.

Any ideas?

Thanks.
Cheers
Felix

If the pin is an input (not an output), it has to turn on the pullup when you digitalWrite() the pin 1, and turn it off when you write 0.

If the pin is an output, you have to set the pin to output that value (ie, not just via the pullup).

That’s partly a compatibility thing with the AVR boards - on the AVRs, the hardware behaves like this without any logic - you set the appropriate bit of PORTx, and if DDRx is set output, PORTx controls the output drivers, if it’s input, it controls the pullup. So they had to make digitalWrite() on the Due behave the same way.

That’s why there’s that bit of logic in the code.

My guess with the problem you posted the picture of the scope traces from is a problem with either your wiring or your code. As you’ve posted neither, I can’t really suggest anything. Did you forget to set a pin output? Is there a load on the pin?

DrAzzy:
If the pin is an input (not an output), it has to turn on the pullup when you digitalWrite() the pin 1, and turn it off when you write 0.

If the pin is an output, you have to set the pin to output that value (ie, not just via the pullup).

That’s partly a compatibility thing with the AVR boards - on the AVRs, the hardware behaves like this without any logic - you set the appropriate bit of PORTx, and if DDRx is set output, PORTx controls the output drivers, if it’s input, it controls the pullup. So they had to make digitalWrite() on the Due behave the same way.

That’s why there’s that bit of logic in the code.

Thanks for the explanation. It is quite unusual to switch an input as an output but why not. It then doubles with the INPUT_PULLUP mode.

DrAzzy:
My guess with the problem you posted the picture of the scope traces from is a problem with either your wiring or your code. As you’ve posted neither, I can’t really suggest anything. Did you forget to set a pin output? Is there a load on the pin?

I did not put the code because it is quite complex here the summary:

At init:

#define PIN_CS_C 45
void spi_mgr_init(void){
 digitalWrite(PIN_CS_C, HIGH);
 pinMode(PIN_CS_C, OUTPUT);
 digitalWrite(8, LOW);
 pinMode(8, OUTPUT);
}

In 100ms task:

void spi_mgr_adc(void){
 spi_mgr_adc(PIN_CS_C,PIN_CONV_C,0);
}

void spi_mgr_adc(int cs_pin, int conv_pin, byte channelnum){
 byte l_adc0, l_adc1;
 // First stop conversion, then start receiving data
 adcext_stop_conversion(conv_pin);
 //digitalWrite(23, LOW);
 delayMicroseconds(5);
 noInterrupts();
 SPI.beginTransaction(SPISettings(400000, MSBFIRST, SPI_MODE1));
 digitalWrite(cs_pin, LOW);
 digitalWrite(8, LOW);
 l_adc0 = SPI.transfer(0);
 l_adc1 = SPI.transfer(0);
 digitalWrite(cs_pin, HIGH);
 digitalWrite(8, HIGH);
 SPI.endTransaction();
 debug_println(String(cs_pin) + "=" + String(l_adc0) + String(l_adc1));
 adcext_push_Value(channelnum, (word)((l_adc0 << 8) + l_adc1));
 interrupts();
}

So you see that pin 45 and pin 8 are handled exactly the same way in the code.

I first thought that I killed the low side FET of the push-pull output stage of the SAM3X but then I did an experiment using ShiftOut which worked:

shiftOut(8, 22, MSBFIRST, so_data);

There it works! The bits have sharp edges using exactly the same HW.

About the hardware:
I tried out several loads on pin 8:

  • no load
  • 1k to GND
    There is no difference.
    Pin 45 (chip select) is connected to a 74LVC125.

I found the issue.
It was as usual something simple. In fact the outputs which did not work were not initialized using pinMode(8, OUTPUT);
In was in the code but not executed.

Now what made it difficult is the unusual behavior of the Arduino libraries switching the pull-up on or off on an input when trying to write to it as an output. So it seems to work at a first glance but looking in detail it does not. This behavior is unusual and I do not understand the use case.
For me either something is an output or it isn't the processor architecture allows reading back an output as an input anyhow. The pull-ups are something usually programmed at init but not in runtime.

What is the use case of this function of digitalWrite(...) changing the pull-up configuration if it is an input?