DAC resolution

Hi! I wanted to have a analog output with arduino, so I am using a DAC MCP4901 (8bit).
I used the Library
<DAC_MCP49x1.h> found online.
I set the dac.setSPIDivider(SPI_CLOCK_DIV32); SO the clock shoud have a frequency of 16MHz/32= 500KHz.
The arduino is measuring first a sensor value and the ADC clockfrequency is set at 666KHz.
If the DAC is 8bit the resolution should be 5V/(2^8)=0,02V, but I have a resolution of 0,2V!
The scale are taller and less in number as they should be!
_
|_
|_
|_
|_
|_
| For a amplitude of 2.5 I have ~13steps

Do you know why?
Thank you!!

Maybe you could get better help if you posted your code.

According to the datasheet, the ADC has a minimum conversion time of 13 us.
You're aiming for half that.

Where did you see that exactly? What I know is that the ADC makes the faster conversion in 1.6us.
16000000/13/2
13 clock cycle for one conversion.

For me it is ok also a minimun conversion time of 52us, but I am using the one of 13us.
Can you indicate me where is that part in the data sheet?
Can you explain me better what do you mean by

you're aiming for half that ?

Where did you see that exactly?

As I wrote, it's in the datasheet, section 23 (it's page 251 in my copy), under the header "23.1 Features"

Can you explain me better what do you mean by
Quote

you're aiming for half that ?

13 us divided by two is 6.5 us.

What I know is that the ADC makes the faster conversion in 1.6us.

...and also less precise.

By default, the successive approximation circuitry requires an input clock frequency between 50 kHz and 200 kHz to get maximum resolution. If a lower resolution than 10 bits is needed, the input clock frequency to the ADC can be higher than 200 kHz to get a higher sample rate.

Thank you I found it, in my copy it was in chapter 24.

So what are you saying is that I want an ADC with 6.5 that is half of what the data sheet is advicing that's why it doesnt have a good accurancy? I tryed also with a prescaler of 64 for so a convertion time of 52us but the step size stayed the same. :confused:

I'm a little unclear (perhaps because you didn't post your code or a clear description of your connections), but I'd take it one subsection at time, to figure out where you problem lies.
I don't have any of your hardware, so it's over to you.

Well I have no idea what you are actually doing but that output to the DAC looks wrong.
In essence you read two bytes to make a 16 bit value and you send a 14 bit value to an 8 bit DAC.
I would replace the writes to the DAC with serial print statements so you can confirm that the numbers you are sending out to the DAC are what you expect them to be.

You have not posted a link to your mysterious libiary.

Hi,

Does the library for the DAC have examples?
If so have you tried them to make sure your connections and the DAC is working?

Tom.... :slight_smile:

That's your code? I don't see anything in the sketch that will change the ADC clock prescaler from the default value of 128. Just to be sure, I deleted everything in your code that required the library, added a line to print the value of ADCSRA at the end of setup(), and ran it. The value was 0x87 - ADC enabled, clock scaler set to 128, for a 108 usec conversion time. If this is really the code you're talking about, it's not doing what you say it does.

I can't tell tell what the purpose of this code is, either. It looks like it might be to send data to the DAC, and read it through the ADC. Or maybe the data flows the other way? At any rate, if you think that the ADC is running fast, you may be mistaken; that would affect your interpretation of the output.

You don't say what makes you think that the DAC delivers output in 0.2V steps. Have you sent it multiple values, holding each one long enough to get a reading on a voltmeter, or are you relying on this code to get that result?

here you find the library and the example of the DAC device.

TomGeorge:
Hi,

Does the library for the DAC have examples?
If so have you tried them to make sure your connections and the DAC is working?

Tom.... :slight_smile:

Yes it is working! if I send 0 and 255 it gives me an output of 0 and 5V.

tmd3:
That's your code? I don't see anything in the sketch that will change the ADC clock prescaler from the default value of 128. Just to be sure, I deleted everything in your code that required the library, added a line to print the value of ADCSRA at the end of setup(), and ran it. The value was 0x87 - ADC enabled, clock scaler set to 128, for a 108 usec conversion time. If this is really the code you're talking about, it's not doing what you say it does.

I can't tell tell what the purpose of this code is, either. It looks like it might be to send data to the DAC, and read it through the ADC. Or maybe the data flows the other way? At any rate, if you think that the ADC is running fast, you may be mistaken; that would affect your interpretation of the output.

You don't say what makes you think that the DAC delivers output in 0.2V steps. Have you sent it multiple values, holding each one long enough to get a reading on a voltmeter, or are you relying on this code to get that result?

ADCSRA |= (1 << ADPS2) | (1 << ADPS1)| (1 << ADPS2); //prescaler 128, one conversion every 0.1ms
i commented but this is the command. And one arduino read ADC sensors data and it sends trow serial to the other arduino who puts the data received in the DAC. My question intact is why the DAC delivers output in 0.2V step if the theory says that 8bit dac resolution should be Vref/2^8. and the value that I am giving to the dac are int variable (16bit) that reads the SerialRead. the value should occupied 10bit, that I am scaling with bitshit >> 2. I think that is correct.

maybe here the problem is that the ADC of arduino is too fast for my sensor signals. or the 8bit solution is too small.

Grumpy_Mike:
Well I have no idea what you are actually doing but that output to the DAC looks wrong.
In essence you read two bytes to make a 16 bit value and you send a 14 bit value to an 8 bit DAC.
I would replace the writes to the DAC with serial print statements so you can confirm that the numbers you are sending out to the DAC are what you expect them to be.

You have not posted a link to your mysterious libiary.

tomorrow I will see the value of adcrx thanks for the idea.

thanks all for the answers

Take note that this statement -

  ADMUX |= (1 << ADLAR); //left align the ADC value- so we can read highest 8 bits from ADCH register only

has no effect. In the sketch, ADLAR is set in setup(), but this statement in analogRead(), from wiring_analog.c, clears it before starting the conversion:

	ADMUX = (analog_reference << 6) | (pin & 0x07);

Results of analogRead() will be right-justified, regardless of the state of ADLAR when analogRead() is called.

This statement from the sketch -

  //ADCSRA |= (1 << ADPS2) | (1 << ADPS1); //prescaler 128, one conversion every 0.1ms (1 << ADPS2) |

doesn't have any effect, either, because it's a comment. If it were an active statement, it wouldn't do what the comment says - select a prescaler of 128. It also doesn't do what you describe in your posts - set the analog conversion time to 13 or 6.5 usec. That statement selects prescaler 6, which is 64, so the ADC conversion time would be 16000000/13/64 = 52 usec, if it was free running; 54 usec for a single conversion.

In addition to the course that Grumpy_Mike suggested - Serial.print()'ing data to verify that it's what you expect - I'd recommend that you modify the 0V/5V example sketch to deliver some intermediate values, and, in particular, some values that are closer together than 0.2V. If you can persuade the DAC to deliver analogs at the appropriate texture in the steady-state, you'll have more information to help isolate your problem.

I used in the same hardware configuration the DAC to have a continuos voltage (i need to do other staff) and as I change value it gave me the right one. for example 127 == 2.5V etc..

ok I notice that ADMUX have no effect.

now my questions are:

How can then have 52us for a conversion?
how/where do I have to set it?
Do I have to change wiring library?
I think is there the problem!!!
In the sample frequency, not in the DAC. I was reading a lot about ripple and resolution.

If I put of the comment and I write

ADCSRA |= (1 << ADPS2) | (1 << ADPS1);

is It setting the prescaler or is it still clear before I do analogRead()?
How can I regolate the free running mode (I don't know how to set this mode)>

I knew it was 64 for I forgot to change the command, I was trying to use different preschooler and to see the difference, but I didn't found a real good one.
tomorrow I will try to deliver some intermediate value. thank you all!!!!

P.s.
For the previous answer, I am putting in A0 a sinusoide that is suppose to take all the value between 0 to 5V i read it with analogRead I send it to the other arduino, it receive it but when i pass the value in the DAC and the latter has to rewrite the wave it has steps of 0.2V.

sorry if I am not clear, language problem.

I did the measurament and for value with 0.2V of difference it separate them well, but for value less then 0.2V it is almost impossibile to see the separation.

Giulialiuyi:
How can then have 52us for a conversion?

the code you posted doesn't have 52 usec for a conversion - it has 108 usec for a conversion. If you executed the ADCSRA assignment in the comment, it would set ADPS2 to 1, ADPS1 to 1, and ADPS0 to 0, with a value of 6. That corresponds to a prescaler of 64, and, at 16 MHz, a conversion time of 54 usec. Note that the ADC takes 13.5 ADC cycles to complete a conversion when it's not in the free-running mode.

how/where do I have to set it?

You set the ADC clock speed using bits ADPS2:0 in register ADCSRA. You can look up the various clock speeds in the table in the datasheet, in the ADC Converter chapter.

Do I have to change wiring library?

I'd recommend against it. But, I don't know what you're talking about here - I can't tell what you'd hope to gain by changing it. If you want different conversion speeds, then change ADPS2:0; if you want ADLAR to have effect, you have to use another method of azquiring the analog, rather than using analogRead(). The two issues are entirely separate from each other.

If I put of the comment and I write

ADCSRA |= (1 << ADPS2) | (1 << ADPS1);

is It setting the prescaler or is it still clear before I do analogRead()?

analogRead() sets the value of ADMUX before it captures the analog reading. In the process of doing that, it always sets ADLAR to 0. If you want to read analog data left-justified, you have to get the analog reading some other way. It's not that big a problem, though - as your own code demonstrates, all you have to do is capture the reading in an integer, and shift it right twice. That'll give you the same data that you would have captured in a byte if ADLAR were set.

How can I regolate the free running mode (I don't know how to set this mode)

You can read the datasheet. That exlains all the ADC registers, and how to use each one. If you want to see an example using the free-running mode without interrupts, there's one: Openmusiclabs FFT.

For the previous answer, I am putting in A0 a sinusoide that is suppose to take all the value between 0 to 5V ...

What generates that sinusoid? Are you inputting a signal to something, or are you generating it in software?

... i read it with analogRead I send it to the other arduino, it receive it but when i pass the value in the DAC and the latter has to rewrite the wave it has steps of 0.2V.

There are a lot of devices and processes in that chain, any of which could be going astray. To test the performance of the DAC, I recommend that you eliminate the rest of the processes, and just use one Arduino to write directly to the DAC. Se how it performs.

I did the measurament and for value with 0.2V of difference it separate them well, but for value less then 0.2V it is almost impossibile to see the separation.

I don't know what that means. Try using code 123 in the DAC. Measure the voltage. Then, use 124, and measure. Record the voltages, and report them to us, for codes 123, 124, 125, 126, 127, 128, 129, 130, 131, and 132, and 133.

I write the goog 64prescaler. "With it would set ADPS2 to 1, ADPS1 to 1, and ADPS0 to 0, with a value of 6"

I am generating the sinusoide with a HP waveform generator.

did the measurament and for value with 0.2V of difference it separate them well, but for value less then 0.2V it is almost impossibile to see the separation.

To explain better this sentence is when I took value of 127, 126, 128 I have the same analog output result of 2.5V. It changes when I put difference of 10. It means it gives me a different output value for 127 or 137 or 117. they need to have a separation of 10.

I solve my problem adding a software filter with the oscilloscope. Thank you for all the information, Understanding the data sheet is a bit harder for a no English mother tounge. It is very schematic and it required a lot of effort. Reading the forum tough with examples clearify a lot the information.

Thank you for the answers.