Writing a variable to a PORT - what variable type is needed?

Hardware: Mega2560
IDE: AS7

I'm having issues declaring the correct type of variable, and probably other issues I'm not aware of.

Short Term Objective: Read PORTA, invert the entire port values, write the result to PORTC.

I thought I could read PORTA and assign the value to the variable 'portAread', invert it using ~(portAread), and then just write that value to PORTC. That is not how it is done, apparently.

Long Term Objective: Read PORTA, compare to previous read using bitwise operators. Then depending on the result, write it to either PORTC or PORTL.

I can write the binary value of portAread to the serial monitor, but can't seem to invert it and/or write it to PORTC.

The ports are wired together: C0 (output) is connected to A0 (input), C1 to A1, etc. I'm doing this to demonstrate the functionality of the code.

The way I've written my code, I thought I would see the value of portAread cycle between 0b00000000 and 0b11111111. That is not happening - it stays at 0b11111111.

Any direction is very appreciated.

byte pinState = LOW;
byte lastPinState = HIGH;

unsigned int portAread;



byte pin = HIGH;

unsigned long firstTime;
unsigned long secondTime;
unsigned long resultTime;

int count = 0;


void setup()
{
 //lcd.begin(8, 1);

 
 Serial.begin(57600);
 
 
 DDRA = B0000000; // Port A is INPUT
 PORTA = B11111111; // Sets pull-up

 DDRC = B11111111; // Port C is OUTPUT
 PORTC = B00000000; // Clear Port C 
 
 pinMode(13, OUTPUT);

}

void loop()
{ 
 
 // Visual confirmation it's not hung.
 pin = !pin;
 digitalWrite(13,pin);
 
 
 
 firstTime = micros();
 
 // ***** Suspected problem is with next 3 lines, and probably the way the variable was declared. **********
 
 portAread = PORTA; // Read PORTA into variable
 
 portAread = ~(portAread); //Invert values.
 
 PORTC  = portAread; // Write variable to PORTC
 
 secondTime = micros();
 resultTime = secondTime - firstTime;
 





 Serial.print(count);
 Serial.print("\t");
 Serial.print(portAread, BIN);
 Serial.print("\t");
 Serial.println(resultTime);

 ++count;
  
delay(1500);
}

The way I've written my code, I thought I would see the value of portAread cycle between 0b00000000 and 0b11111111. That is not happening - it stays at 0b11111111.

Each port is controlled by three registers, which are also defined variables in the arduino language. The DDR register, determines whether the pin is an INPUT or OUTPUT. The PORT register controls whether the pin is HIGH or LOW, and the PIN register reads the state of INPUT pins set to input with pinMode().

DDR and PORT registers may be both written to, and read. PIN registers correspond to the state of inputs and may only be read.

A has been set up as an input register with the pull ups enabled. To read the state of the inputs, you will need to use PINA.

@cattledog -

Well... that would be the problem. Can't see the forest through the trees, apparently.
Thanks for setting me straight on that.

When I serial print the variable, it is 16 bits wide (makes sense, since I declared it an INT), but the MSBs were all 1s.

I changed the variable type to byte, and all is well.

Thank you again - greatly appreciated.

ih55t:
Well... that would be the problem. Can't see the forest through the trees, apparently.

The following diagrams may help you to see something more on the structure of the IO ports of MCU.

pd2x.png
Figure-1: Internal Structure of 1-Bit Output Port

ddq.png
Figure-2: Internal Structure of 1-Bit Input Port

Any follow up questions/queries would be highly welcomed.

pd2x.png

ddrdx.png

Thanks for the diagrams - nice to see the hardware side of it.
It appears that even if a port is configured as an output, the PINx function is still valid. You could read an output port - but probably not recommended, depending on the application. The outside world could force the pin to a state other than what was written.

Thanks,
Mike

You can read from a pin set as OUTPUT - it causes no problems. Of course it should give you the same value as was written to the pin. Trying to use an external device to drive a pin set as OUTPUT is likely to lead to damage to the Arduino.

You can also write to PINx registers. Quote from Atmega 328 datasheet

However, writing a logic one to a bit in the PINx Register, will result in a toggle in the correspond-
ing bit in the Data Register.

When writing directly to a PORT be aware that some of the pins in some of the ports (all of the ports on an Atmega 328) have special functions and should not be disturbed.

...R

I was remembering reading somewhere about read/write errors, but it probably doesn't have application here.

Read/Modify/Write errors: Read Modify Write Problem
Seems to be a PIC thing.

Agreed - driving the pin externally will certainly kill it. For driving higher power devices, I use opto-couplers and/or gate drivers.

Thanks for the info.

Robin2:
Of course it should give you the same value as was written to the pin.

It may not be always true?

If Logic High value of the output pin drives a low impedance load that causes VOH to drop below 3.0V (VIH of Atmega328P is 3.0V, Fig-1 of Post#3), reading the logic value of the output pin will (most probably) return LOW; whereas, Logic High was written at that output pin.

Under the above condition, the only way to toggle the logic level of the output pin is to read the value of the internal port latch (for example: bool n = bitRead(PORTD, 2);), invert n, and then write it (inverted value of n) onto the output pin.

GolamMostafa:
It may not be always true?

If Logic High value of the output pin drives a low impedance load that causes VOH to drop below 3.0V

In that case the excess current draw will probably damage the Atmega 328

...R

Robin2:
In that case the excess current draw will probably damage the Atmega 328

It will probably not happen if the designer is aware of his load impedance so that the maximum 'pin power dissipation' remains within rated power which is 84 mW (VOHIOH = 4.2 V20 mA).

GolamMostafa:
It will probably not happen if the designer is aware of his load impedance so that the maximum 'pin power dissipation' remains within rated power which is 84 mW (VOHIOH = 4.2 V20 mA).

So what current would need to be drawn from an Arduino I/O pin in OUTPUT mode to drop the voltage low enough to be detected as a LOW?

...R

Robin2:
So what current would need to be drawn from an Arduino I/O pin in OUTPUT mode to drop the voltage low enough to be detected as a LOW?

As a rough calculation:

Assume VOH = 2.50V
The the IOH = 84 mW/2.50V = 33.6 mA.
Load Impedance = 74 ohm.

Will this 33.6 mA current at 2.50V is going to cause a thermal runaway for the target digital pin?

We often say that a pin can drive 20mA. That specification is in the datasheet. But what it is really saying is that if you draw 20mA then the voltage will remain above the HIGH threshold voltage.

That is why you can sometimes draw 40mA but it is not guaranteed to be above HIGH with that load.

GolamMostafa:
Will this 33.6 mA current at 2.50V is going to cause a thermal runaway for the target digital pin?

This is a complete distraction from the OP's question - you should have started your own Thread for your questions.

What makes you think that 33.6mA will cause the Atmega 328 output voltage to fall to 2.5v? AFAIK the Atmega 328 will try to hold the voltage HIGH until the device fails.

...R