wrong resolution of PORTD

Hi Arduinos!
I’am constructing a square wave biphasic generator. I am able to generate a signal, that user can adjust in a simple Matlab program. Generated digital signal is processed with DAC and than I use voltage reference of 2.5V to decrease signal form 0-5V to -2.5-2.5V My code is done, but I have a little problem with PORTD output.

I defined my PORTD to use 6 pins as output, these pins are pins 2-7.
That means, that if I connect DAC to my pins, I should be working with resolution of 6^2=64.

But I am not! My oscilloscope tells me different. I get maximum values of voltage when I set PORTD to 255, not 63. And this I cannot solve. Can you help me? What am I doing wrong?

Thank you!

My code:

int amplitude; 
int width;
int frequency;
int time;
int ele1;
int ele2;
int ele3;
int ele4;
int B=B;
byte binary;
float pause;
float wave;
float freq;
unsigned long count = millis(); 

void setup() 
{
  Serial.begin(9600); 
}

void loop() 
{
  PORTD=127;
  while (Serial.available()>0) 
  {
    int amplitude=Serial.parseInt(); 
    int width=Serial.parseInt(); 
    int frequency=Serial.parseInt();  
    int time=Serial.parseInt(); 
    int ele1=Serial.parseInt(); 
    int ele2=Serial.parseInt(); 
    int ele3=Serial.parseInt(); 
    int ele4=Serial.parseInt(); 
    if (Serial.read() == 'n') { 
      if (ele1+ele2+ele3+ele4 > 0 ){ 
        amplitude = map(amplitude, 0, 80, 0, 127); 
        time=time*1000; 
        freq=(1.0/(float)frequency)*1000.0*1000.0; 
        wave = (float)width/2.0;
        pause=(float)(freq-width)/1000.0;

        binary=ele1| ele2<<1 | ele3<<2 | ele4<<3 | 0<<4 | 0<<5 | B<<6 | 0<<7; 
        DDRD = DDRD | B11111100; 
        DDRB=binary; 
        PORTB=binary; 


        while ((millis()-count) < time) {  
          PORTD=127+amplitude; 
          delayMicroseconds(wave); 
          PORTD=127-amplitude; 
          delayMicroseconds(wave); 
          PORTD=127; 
          delay(pause); 
        }
        PORTB=0B000000; 
      }
    }
  }
}

sounds like you're expecting the lower 6 bit to be used, but it's the higher 6 bits that are used. so 255 will give the highest value, but will be equal to 254, 253 and 252. so just shift your values to the left by 2, and it will work as expected?

No it's not that. My logic is: If I'm using 2 pins, 5V voltage should occur on DAC output when I write: PORTD=3 (because 2^2=4) when using 3 pins, 5V voltage should occur on DAC output when I write: PORTD=7 (because 3^2=8) when using 4 pins, 5V voltage should occur on DAC output when I write: PORTD=15 (because 4^2=16) when using 6 pins, 5V voltage should occur on DAC output when I write: PORTD=63 (because 6^2=64)

but in my case its 255 as if I was using 8 output pins..

can anybody explain please?

My logic is:
If I'm using 2 pins, 5V voltage should occur on DAC output when I write: PORTD=3 (because 2^2=4)
when using 3 pins, 5V voltage should occur on DAC output when I write: PORTD=7 (because 3^2=8)
when using 4 pins, 5V voltage should occur on DAC output when I write: PORTD=15 (because 4^2=16)
when using 6 pins, 5V voltage should occur on DAC output when I write: PORTD=63 (because 6^2=64)

That depends on which 2, 3, 4, or 6 pins you use on that port. Quit handwaving, and start explaining.

On an UNO, pins 2 to 7 are Port D bits 2 to 7. And you should only change those bits.

Value 3 is bits 0 and 1, not bits 2 and 3. Those are 3 << 2.

Paul, in first post I stated, that I am using pins 2-7 of PORTD. You can check it in code as well.

which means that highest value (value of Vout would be 5V on oscilloscope placed after DAC) should be, when I write PORTD=63, not PORTD=255 as it is now. Since I'm using only 6 pins of PORTD, not 8.

thanks

No No NO!

You are connecting to PortD bits 2 to 7, not 0 to 5. Your LSB is NOT the port LSB but port LSB + 2.

PortD Pin ------- output line 2 ------------------ 0 3 ------------------ 1 ..... 7 ------------------ 5

All pins to your output HIGH is not 63, it is 252. In HEX; not 0x3F, but 0xFC. In BIN; not 0b111111, but 0b11111100.

BTW, when you write bots 0 and 1 of PortD, those are the Serial pins. It's not brilliant to write those and expect other pins to change.

you need something like this?

void writeADC(uint8_t val)  // val = 0..63
{
  PORTD = (PORTD  & 0x03) |  (val << 2);  // clear all the ADC bits and set the new ones. (shifted 2 positions to use bit 2..7
}

GoForSmoke,

BTW, when you write bots 0 and 1 of PortD, those are the Serial pins. It's not brilliant to write those and expect other pins to change.

yes, thats why I'm using only 6 bits instead of 8.

Thank you for the answer and it brought some light over my question, but I still don't get it all :drooling_face:

When my pins 0 and 1 are NOT outputs, why is it using 256 resolution? (0-252) instead of 64? I'm writing my bachelor now, and I just don't know how to explain it.

Someone’s given you working code already. This is nothing to do with resolution, its
because you are writing the wrong pins. To repeat:

  PORTD = (PORTD  & 0x03) |  (val << 2);  // clear all the ADC bits and set the new ones. (shifted 2 positions to use bit 2..7

kakaovnik:
GoForSmoke,

BTW, when you write bots 0 and 1 of PortD, those are the Serial pins.
It’s not brilliant to write those and expect other pins to change.

yes, thats why I’m using only 6 bits instead of 8.

Thank you for the answer and it brought some light over my question, but I still don’t get it all :drooling_face:

When my pins 0 and 1 are NOT outputs, why is it using 256 resolution? (0-252) instead of 64?
I’m writing my bachelor now, and I just don’t know how to explain it.

The full Port has 8 bits resolution but you only use the TOP 6 bits.

port bit — value
0 1 ---- this is SERIAL, do not change this bit
1 2 ---- this is SERIAL, do not change this bit
2 4 --------- this is your bit 0
3 8 --------- this is your bit 1
4 16 --------- this is your bit 2
5 32 --------- this is your bit 3
6 64 --------- this is your bit 4
7 128 --------- this is your bit 5, you only use 6 bits for 6 lines that start at port bit 2

To write 7 to your output means set your bits 0, 1 and 2, NOT port bits 0, 1 and 2.
You chose the pins, you must offset your data to start on the pins you chose.
Left shift command ( << ) moves all the bits in a number to higher positions.
You need to shift your output 2 bits higher. Either left shift 2 or multiply by 4.

I want to kiss you for this, man! THANK YOU! ;)

When you learn to use pointers you will see this same principle of offsets on the byte(s) level. It is like measuring meters from 2 different places along the same line. Array data can be seen as a line of bytes or groups of bytes. Every byte is a line of 8 bits, works the same.

one more question:

Is it possible to set maximum value of PORTD by myself?

it would be great, if 5V voltage would occur after I put 62 not 63 as it is now.

you can use constrain() in software, in code

value = constrain(value, 0, 62); // limits value between 0…62

it works like this

constrain(value, minimum, maximum)
{
if (value < minimum) return minimum;
if (value > maximum) return maximum;
return value;
}

but is implemented as a macro IIRC

Depending on the bounds (least to most) and the variable type and range, you can't 'do the math' and then check bounds.

Variable type byte: range is 0 to 255. My operation uses the full 0 to 255.

If the value is 250 and the operation would add 10 then adding 10 and checking bounds would fail. So check instead if the value is more than the range maximum minus what is to be added and if so make the result equal to the maximum, else the result is the addition. If there will be a subtraction then check that the variable value is more than or equal to the number to be subtracted and if so then the result is the subtraction else the result is zero.

The programmer is responsible for the result. Don't step on it and then make it fit.